懒加载单例模式

懒加载指的是,加载类的阶段不产生出单例对象,在运行时需要单例对象的时候再实例化出单例对象。

错误代码:

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个步骤的:

  1. 内存中分配对象空间。
  2. 内存初始化为零值。
  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;
}
}