懒加载指的是,加载类的阶段不产生出单例对象,在运行时需要单例对象的时候再实例化出单例对象。
错误代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.company;
public class LazyClass {
private static LazyClass instance = null; private LazyClass(){}
public static LazyClass getInstance(){ if (instance == null) { synchronized (LazyClass.class){ if (instance==null) { instance = new LazyClass(); } } } return instance; } }
|
可以看到这段代码的单例对象instance没有加 volatile关键字,这就会导致如下问题。
在语句instance = new LazyClass()执行的时候,是分为3个步骤的:
- 内存中分配对象空间。
- 内存初始化为零值。
- 内存地址赋给
instance引用。
值得注意的是,以上3个步骤执行的顺序是不定的。因为有指令重排序这一机制的存在。假如说,线程A执行到第一个判空之前停止,线程B进入同步代码块,然后执行上述三个步骤中的1,3,然后线程A获得执行权,那么B会判空失败,直接返回instance。
究其原因,主要是指令重排序的锅。因此要禁止指令重排序。即,使用volatile修饰instance。这样就可以保证 1,2这2个步骤是绝对不会跑到3这个步骤后面执行的。
正确代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.company;
public class LazyClass {
private static volatile LazyClass instance = null;
private LazyClass(){}
public static LazyClass getInstance(){ if (instance == null) { synchronized (LazyClass.class){ if (instance==null) { instance = new LazyClass(); } } } return instance; } }
|