纷享双重校验锁单例为何这么实现 2019-08-07
代码实现
|
|
开始问为什么
为什么 instance 需要 static 修饰
单例的访问一般是通过类名进行,在该例中,如果要获取实例对象方法是 DoubleCheck.getInstance() ,即通过静态方法去获取,静态方法只能访问静态变量,因此 instance 必须是静态变量。
为什么需要第一个判空
此次判断可以在类初始化完成后不再进入同步代码块去等待获取锁,进而去初始化。提高效率。
为什么需要 synchronized 加锁
此处加锁可以将同步块降低为最小粒度。多线程条件下,同步块越小,效率越高。加锁之后进行初始化,防止其他线程重复初始化。
同步块中为什么需要再次判空
线程之间的切换是由 CPU 决定的。如果线程 A 在 执行完 new DoubleCheck() 后,还未赋值给 instance ,此时发生了线程切换。线程 B 在第一次判断时会发现 instance 是 null ,这样就进入同步代码块等待锁释放。然后再切换到线程 A 执行,赋值给 instance 并释放锁。这时线程 B 获取锁,如果没有第二次判空,则会将 instance 再初始化一次。
为什么 instance 需要 volatile 修饰
关键字 volatile 在这里使用是为了保证变量 instance 的可见性。即 new DoubleCheck() 初始化类完成之后,赋值给 instance ,此时对其他线程在拿到 instance 时肯定是非 null 的。如果不加 volatile 关键字,线程 A 在释放锁之后,instance 变量仅存在了 CPU 缓存中,而没有写入主存,此时线程 B 获取锁并判断 instance 为空,会再次初始化。