摘录加总结------
(1)传统的单例模式的双检锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class Singleton { private static Singleton sInstance; public static Singleton getInstance() { if (sInstance == null ) { //① synchronized (Singleton. class ) { //② if (sInstance == null ) { sInstance = new Singleton(); //③ } } } return sInstance; } private Singleton() {} } |
双检锁的设置可以避免在1和2位置处,在并发时假如A线程和B线程都进入了1位置,但是A获取到了锁,new了对象之后,B获取到锁之后又重复new一个实例,但是仍然在3位置处可能会有以下问题,即指令重排的问题:
new一个实例时,会分配实例内存大小,初始化对象,设置引用指向的过程,但是如果指令重排之后就有可能像上图那样子,线程A分配实例内存大小,设置了引用指向内存空间,但是B线程由于判断到引用不为空,就会在A线程初始化对象之前访问到一个空的对象。而不是再new一个对象。所以需要在实例属性前面加上volatile关键字,保证线程可见性,通过设置内存屏障方式禁止指令重排。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | public class Singleton { private static volatile Singleton sInstance; public static Singleton getInstance() { if (sInstance == null ) { synchronized (Singleton. class ) { if (sInstance == null ) { sInstance = new Singleton(); } } } return sInstance; } private Singleton() {} } |