深入浅出设计模式——单例模式

Posted 苏凌峰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入浅出设计模式——单例模式相关的知识,希望对你有一定的参考价值。

1.单例模式介绍

2.用代码演示单例模式

3.总结

1.单例模式介绍

定义:所谓单例模式,就是采取一定的方法保证在整个软件系统中,对某个类只能存在一个实例,并且该类只提供一个获取其对象实例的方法(静态方法)。

描述:
有些时候,我们系统只需要一个全局的对象,这样有利于我们协调系统的整体行为,节约资源。
比如说:
1)在某个服务中,需要一个对象来读取配置文件,那么有一个这样的对象来读取配置文件也就足够了。
2)比如说我们现在要提供一个线程池,那么大部分情况下线程池这种比较重量级的对象,几个功能模块统一有一个也就够了,因为他会有一个常驻线程数,如果线程池多了会浪费资源,所以这种对象也适合单例的。

以上这两种情况只用了单例的对象,节约了资源,简化了管理。

2.用代码演示单例模式

基本的实现思路
可以看出,单例模式要求一个服务只有一个这样的对象(永远都是相同对象),而且只有一个获取该实例的方法。

根据这个要求,主要实现有以下两个步骤:

1)将构造方法私有化,这样其它的代码就无法调用到该类的构造方法,就无法使用new的方法来创建对象,只能通过该类提供的静态方法来得到该类的唯一实例。

2)在该类内提供一个静态方法,当我们调用这个方法的时候,就能返回单例对象的引用。

单例模式一共有8种写法,
1)饿汉式(静态常量)
2)饿汉式(静态代码块)
3)懒汉式(线程不安全)
4)懒汉式(线程安全,同步写法)
5)懒汉式(线程安全,同步代码块)
6)双重检查
7)静态内部类
8)枚举

我们来每一种写法都来介绍一遍:

1)饿汉式(静态常量 可用)

public class Singleton {

    private static Object target = new Object();

    private Singleton() {
    }

    public static Object getInstance(){
        return target;
    }

}

优点:写法比较简单,就是在类加载的时候就已经完成了实例化。
缺点:在类装载的时候就完成初始化,没有达到lazy Loading的效果。如果从始至终都未用过这个实例,则可能会造成内存浪费。

2)饿汉式(静态代码块 可用)

public class Singleton {

    private static Object target = new Object();

    private Singleton() {
    }

    public static Object getInstance(){
        return target;
    }
}

这种方式其实和第一种类似,优缺点也是一样的。

3)懒汉式(线程不安全 不可用)

public class Singleton3 {

    private static Object target =  null;


    private Singleton3() {
    }

    public static Object getInstance(){
        if(target==null){
            target = new Object();
        }
        return target;
    }

}

优点:起到了懒加载的效果,可以或多或少节约一点资源。

缺点:只能在单线程的模式下,才适用,多线程可能会创建多个单例对象,造成资源浪费。(线程A进入了if判断之后,时间片结束,另一个线程B也通过了这个语句,然后又调度线程A,继续又创建了一个对象,就产生了多个实例。)

4)懒汉式(线程安全,同步写法 不推荐使用)

public class Singleton4 {

    private static Object target =  null;


    private Singleton4() {
    }

    public static synchronized Object getInstance(){
        if(target==null){
            target = new Object();
        }
        return target;
    }
}

优点:有懒加载的效果,又线程安全。

缺点:效率太低了,不仅仅创建的时候,每个线程想获得实例的时候,都要进行同步,其实只要在要实例的时候进行同步就行了,如果对象已经存在的话直接返回就好了。

5)懒汉式(线程安全,同步代码块 不可用)


public class Singleton5 {

    private static Object target = null;


    private Singleton5() {
    }

    public static Object getInstance() {
        if (target == null) {
            synchronized (Singleton5.class) {
                target = new Object();
            }
        }
        return target;
    }

}

优点:优化了第四种方案,可以让获取对象的速度变快
缺点:还是会有线程安全问题,(线程A进入了if判断之后,时间片结束,另一个线程B也通过了这个语句,然后又调度线程A,继续又创建了一个对象,就产生了多个实例。)

6)双重检查 推荐使用

public class Singleton6 {

    private static Object target = null;


    private Singleton6() {
    }

    public static Object getInstance() {
        if (target == null) {
            synchronized (Singleton6.class) {
                if (target == null) {
                    target = new Object();
                }
            }
        }
        return target;
    }

}

优点:进行了两次检查之后,就不怕多线程同时读取的情况了。

7)静态内部类 推荐使用

public class Singleton7 {


    private static class SingletonInstance {
        private static final Object target = new Object();
    }


    private Singleton7() {
    }

    public static Object getInstance() {
        return SingletonInstance.target;
    }

}

优点:类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

8)枚举 推荐使用

public enum SingletonEnum {

    INSTANCE;
    
    public void doSomething() {
        System.out.println("doSomething");
    }

}

优点:保证了安全性和唯一性。
缺点:所以在实际项目开发中,很少见人这么写过

3.总结

单例模式应该是设计模式中最简单的一种模式了。它有以下几个要素:
1)私有的构造方法
2)指向自己实例的私有静态引用
3)以自己实例为返回值的静态的公有的方法

至于使用哪种方式获取,一般视情况而定。

以上是关于深入浅出设计模式——单例模式的主要内容,如果未能解决你的问题,请参考以下文章

深入理解设计模式-单例模式

深入单例模式 - Java实现

深入理解设计模式-单例模式(饿汉单例模式懒汉单例模式双锁单例模式)

单例模式深入探讨

深入浅出设计模式系列 -- 单例模式

深入理解单例模式