上章节我们在懒汉式的单例模式上解决了多线程安全的问题,但解决问题的同时,新的问题也随之而来。
上节问题:
1、在静态方法(static)上添加关键字(synchronized同步锁),就是相当于在类上加锁,锁的范围大,损耗性能。
2、加锁、解锁过程消耗资源。
那么,我们该如何解决呢?
1 public class lazyDoubleCheckSingleton { 2 3 private static lazyDoubleCheckSingleton lazyDoubleCheckSingleton = null; 4 5 private lazyDoubleCheckSingleton() { 6 7 } 8 public static lazyDoubleCheckSingleton getInstance() { 9 if (lazyDoubleCheckSingleton == null) { 10 synchronized (lazyDoubleCheckSingleton.class){ 11 if (lazyDoubleCheckSingleton == null){ 12 lazyDoubleCheckSingleton = new lazyDoubleCheckSingleton(); //1、分配内存给这个对象 //2、初始化对象 //3、设置lazyDoubleCheckSingleton,指向刚分配的内存地址
13 } 14 } 15 } 16 return lazyDoubleCheckSingleton; 17 } 18 }
此种方法就是懒汉模式的双重检测式,把锁加在方法里面,只有空的话才会加锁,不为空的话,直接return lazyDoubleCheckSingleton,大大节省了开销,但是这段代码,还存在着两大隐患,分别是9行、12行,首先在9行判断了是否为空,有可能是不为空的,他虽然不为空,但是是在12行还没完成初始化的情况下,12行的代码虽然是一行,确实经历了三个步骤,注释上2和3的顺序可能调换,进行重排序,也就是先指向内存地址,但是还没初始化对象。下面给大家开一个图。
这是单线程,介入多线程呢?
看吧,就出问题了呢,想要解决这个问题,有两种解决方案
1、不允许2、3进行重排序,加入了volatile关键字.
2、允许一个线程进行重排序,但不允许另外线程看到他的重排序。那么我们看看用第一种方案是怎么解决的呢?
/** * Created by sww_6 on 2019/4/10. * 双重检查 * 1、不允许2、3进行重排序,加入了volatile关键字. * 2、允许一个线程进行重排序,但不允许另外线程看到他的重排序。 * 划重点: * 1、在多线程的时候,cpu有共享内存。 * 2、加入了volatile关键字之后,所有线程都能看到共享内存的最新状态,保证内存可见性。 * <p> * 怎么保持内存一致性? * 用volatile修饰的共享变量,在进行写操作的时候,会多出来很多汇编代码,起到两个作用。 * 1、是将当前处理器缓存行的数据写回到系统内存,写回内存的操作,会使在其他cpu里缓存了该内存的数据无效,其他cpu缓存的数据无效了,就会从共享内存同步数据。保证了内存的可见性。 * 主要使用的是缓存一致性协议 * <p> * 优点: * 既兼顾了性能,又兼顾了线程安全。 */ public class lazyDoubleCheckSingleton { private volatile static lazyDoubleCheckSingleton lazyDoubleCheckSingleton = null; private lazyDoubleCheckSingleton() { } public synchronized static lazyDoubleCheckSingleton getInstance() { if (lazyDoubleCheckSingleton == null) { synchronized (lazyDoubleCheckSingleton.class) { if (lazyDoubleCheckSingleton == null) { lazyDoubleCheckSingleton = new lazyDoubleCheckSingleton(); } } } return lazyDoubleCheckSingleton; } }
好了,我们下期见!
原文地址:https://www.cnblogs.com/shenwen/p/10686682.html
时间: 2024-10-14 16:34:01