单例模式

1.饿汉式

最容易写出来的单例模式(Singleton Pattern)。

public class Singleton {
    //指向自己的私有静态引用
    private static Singleton singleton = new Singleton();
    //私有的构造方法
    private Singleton() {
    }
    //返回实例
    public static Singleton getInstance() {
        return singleton;
    }
}

这种写法简单粗暴,对象在单例类被加载时就实例化了一个对象交给自己的引用,即对象在没有使用之前就已经初始化了。如果这个对象很大,没有使用这个对象之前就把它加载到内存中去不太可取。因此对其进行改进,延迟加载(Lazy-load Singleton),即懒汉式。

2.懒汉式

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){
    }
    public static Singleton getInstance() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;       
    }
}

这种方式使用了延迟加载来保证对象在没有使用之前,是不会初始化的。但是,这种写法在多线程时会出现线程不安全。一种简单的线程安全写法如下,加锁Synchronized。

3.简单加锁

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){
    }
    public static Synchronized Singleton getInstance() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;       
    }
}

简单加锁,会使程序的效率降低。如何既提高效率,又线程安全呢?双重检查锁方法如下(Double-Check Lock)。

4.双重检查锁

public class Singleton {
    private static Singleton singleton = null;
    private Singleton(){
    }
    public static Singleton getInstance() {
        if(singleton == null) {
            Synchronized(Singleton.class) {
                if(singleton == null) {
                    singleton = new Singleton();
                }
            }           
        }
        return singleton;       
    }
}

这种写法做到了延迟加载、线程安全,看上去很完美,但这种写法还是有问题的。假设A线程执行到第6行,判断对象为空,于是继续执行到第9行去初始化这个对象,但初始化是需要耗费时间的,但是这个对象的地址已经存在了。此时线程B也执行到第6行,它判断不为空,于是直接跳到13行返回这个对象。但是,这个对象可能还没有被完整的初始化,得到的是一个没有完全初始化的对象。虽然这种几率很低,但也不能排除。
那么有没有更好的写法呢?静态内部类,即可以做到延迟加载,又能保证线程安全,同时不用加锁。

5.静态内部类

public class Singleton {
    private Singleton(){
    }
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;        
    }
}

这五种方式基本已经够用,在不同的情况下选择即可。还有一种枚举的方法,由于用的少,只是记录一下。

6.枚举

public enum Singleton {
    INSTANCE;
    private Singleton() {
    }
    public Singleton getInstance() {
        return INSTANCE;
    } 
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页