Spoon | blog Spoon | blog

"大胆点生活,你没有那么多观众"

目录
双重检查锁
/  

双重检查锁

双重检查锁

在Java程序中,有时候可能需要推迟一些高开销的对象初始化操作,并且只有在使用这些对象时才进行初始化。此时,程序员可能会采用延迟初始化。但要正确实现线程安全的延迟初始化需要一些技巧,否则很容易出现问题

public class UnsafeLazyInitialization {
	private static Instance instance;
	public static Instance getInstance() {
		if (instance == null) // 1:A线程执行
			instance = new Instance(); // 2:B线程执行
	return instance;
	}
}

在UnsafeLazyInitialization类中,假设A线程执行代码1的同时,B线程执行代码2。此时,线程A可能会看到instance引用的对象还没有完成初始化

 public class SafeLazyInitialization {
     private static Instance instance;
     public synchronized static Instance getInstance() {
         if (instance == null){
             instance = new Instance();
         }
         return instance;
     }
 }

对象创建的三步:

  1. 开辟内存空间 :碰撞指针,空闲链表
  2. 内存初始化:设置初值
  3. 指针指向:使用init指针指向对象的内存空间
 memory = allocate(); // 1:分配对象的内存空间
 ctorInstance(memory); // 2:初始化对象
 instance = memory; // 3:设置instance指向刚分配的内存地址

Java中==判断对象是否存在是通过查看是否存在地址空间指向,多线程下以上三步出现乱序会出现,一个线程在第四行判断是以及运行第三步地址指向,但是并未知识初值值,就会产生一次问题,于是双重检查锁的组用体现

 public class DoubleCheckedLocking {                     // 1
     private static Instance instance;                   // 2
     public static Instance getInstance() {              // 3
         if (instance == null) {                             // 4:第一次检查
             synchronized (DoubleCheckedLocking.class) {     // 5:加锁
                 if (instance == null)                    // 6:第二次检查
                     instance = new Instance();           // 7:问题的根源出在这里
                 }                                      // 8
             }                                          // 9
             return instance;                            // 10
     }                                                  // 11
 }

volatile 解决

volatile 通过添加内存屏障是在new是的操作不可读,只有在new操作完成后其他线程才能进行读取,通过这样保证双重检锁的实现

  • new之前添加一个 StoreStore屏障
  • new后边添加一个StoreLocad屏障

标题: 双重检查锁
作者:三季人
地址:https://sanjiren123.ltd/articles/2022/03/19/1647701217579.html
如有错误还请告知Email:san_ji_ren@163.com