懒汉式的单例为什么要使用double check?
单例,我们知道有懒汉式和饿汉式!
为什么懒汉式要进行double check呢?
相关问题:如果使用静态内部类的方式创建单例,有什么好处?(为什么要这么做?)
您的每一个用心回答,都会让这个世界变得更美好一些!
double check或许应该指的是下面这段代码
public class Singleton { private volatile static Singleton instance = null; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) {// 1 if (instance == null) {// 2 instance = new Singleton();// 3 } } } return instance; }
这种叫做双重检查模式(DCL)
第一次是为了减少不必要的同步,提升性能
第二次是在Singleton为空才创建实例 虽然volatile会耗费一点时间但是值得
volatile的作用是避免程序出错 防止指令重排序带来的不良影响。比如
a=1 a=2 a=3
编译器就会省略前面 直接a=3
一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。这个就是指令重排序。
那我们那里可能会发生指令重排序呢,是在instance = new Singleton()这里。这里经过编译不是一个原子操作,他大概分为以下几步
当多线程来的时候可能一个线程正搞完第2步,他不为空了,还没搞第三步,另一个线程就来判断,嗯 不为空?直接修改,这是不对的。
它在单线程,无并发没什么紧要,但是多线程就会有影响,加了这个就能确保程序的正确性
它相比普通的线程安全的懒汉模式的好处是避免了不必要的同步等待的时间
为什么他会省时间呢?
它的缺点就是第一次加载反应会慢一些,还有就是可能会dcl失效。dcl失效我不太懂,但是查阅资料感觉还是和指令重排序或者指令的执行顺序有关。可以看看
https://www.jianshu.com/p/c647fe58b98d
静态内部类的方式创建单例创建单例的好处就是加载Singleton 类的时候不会初始化instance,只有调用getInstance()的时候才加载SingletonHolder并初始化instance。这样不仅能保证线程安全,也能保证Singleton类的唯一性。好处就是你用的时候才去占用内存,省空间。
public class SingleTon{ private SingleTon(){} private static class SingleTonHoler{ private static SingleTon INSTANCE = new SingleTon(); } public static SingleTon getInstance(){ return SingleTonHoler.INSTANCE; } }
下面是复制的
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。
故而,可以看出INSTANCE在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
=============================
那么也就是说缺点就是可能造成进程阻塞。不过创建一个单例一般很快就好了,没啥影响。
double check或许应该指的是下面这段代码
这种叫做双重检查模式(DCL)
第一次是为了减少不必要的同步,提升性能
第二次是在Singleton为空才创建实例 虽然volatile会耗费一点时间但是值得
volatile的作用是避免程序出错 防止指令重排序带来的不良影响。比如
编译器就会省略前面 直接a=3
一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。这个就是指令重排序。
那我们那里可能会发生指令重排序呢,是在instance = new Singleton()这里。这里经过编译不是一个原子操作,他大概分为以下几步
当多线程来的时候可能一个线程正搞完第2步,他不为空了,还没搞第三步,另一个线程就来判断,嗯 不为空?直接修改,这是不对的。
它在单线程,无并发没什么紧要,但是多线程就会有影响,加了这个就能确保程序的正确性
它相比普通的线程安全的懒汉模式的好处是避免了不必要的同步等待的时间
为什么他会省时间呢?
它的缺点就是第一次加载反应会慢一些,还有就是可能会dcl失效。dcl失效我不太懂,但是查阅资料感觉还是和指令重排序或者指令的执行顺序有关。可以看看
https://www.jianshu.com/p/c647fe58b98d
静态内部类的方式创建单例创建单例的好处就是加载Singleton 类的时候不会初始化instance,只有调用getInstance()的时候才加载SingletonHolder并初始化instance。这样不仅能保证线程安全,也能保证Singleton类的唯一性。好处就是你用的时候才去占用内存,省空间。
下面是复制的
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。
故而,可以看出INSTANCE在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。
=============================
那么也就是说缺点就是可能造成进程阻塞。不过创建一个单例一般很快就好了,没啥影响。