单例模式-懒汉式(双重检验)

Posted shenwen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式-懒汉式(双重检验)相关的知识,希望对你有一定的参考价值。

上章节我们在懒汉式的单例模式上解决了多线程安全的问题,但解决问题的同时,新的问题也随之而来。

上节问题:

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; } }

好了,我们下期见!

 

以上是关于单例模式-懒汉式(双重检验)的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之单例模式详解(java)

单例模式,双重校验的懒汉式

设计模式-单例模式

Android 单例模式必知必会

Android 单例模式必知必会

Android 单例模式必知必会