Java单例模式代码实现和DCL和volatile详细解析

Java单例模式代码实现和DCL和volatile详细解析

十月 29, 2020

网上搜一下单例模式发现都没有说的很全面的,所以总结一下

饿汉式

饿汉式没有线程安全问题,在类加载时就会初始化一个对象,每次 getInstance()得到的也是原来的对象。

1
2
3
4
5
6
7
class Singleton {
private static final Singleton singleton = new Singleton();
private Singleton(){}
public static Singleton getInstance() {
return singleton;
}
}

懒汉式

懒汉式才是重点,由于存在线程安全问题,所以懒汉式的实现有更多的细节。
循序渐进来看一下,懒汉式的变化。

初始版本

1
2
3
4
5
6
7
8
9
10
class Singleton {
private static Singleton singleton;
private Singleton(){}
public static Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}

最开始版本在多线程环境下是不安全的,可能会同时new很多次Singleton对象。

直接在方法上加锁

最直接的方法是在方法上直接加上synchronized。

1
2
3
4
5
6
7
8
9
10
class Singleton {
private static Singleton singleton;
private Singleton(){}
public static synchronized Singleton getInstance() {
if (singleton == null) {
singleton = new Singleton();
}
return singleton;
}
}

但是这么做锁的粒度太大,有更加美妙的方案。

细化锁的粒度

把锁加在方法内部。这里就是DCL(DoubleCheckLock)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getInstance() {
if (singleton == null) {
//第一层判断singleton是不是为null
//如果不为null直接返回,这样就不必加锁了
synchronized (Singleton.class) {
//现在再加锁
if (singleton == null) {
//第二层判断
//如果A,B两个线程都在synchronized等待
//A创建完对象之后,B还会再进入,如果不再检查一遍,B又会创建一个对象
singleton = new Singleton();
}
}
}
return singleton;
}
}

最终版本

最后还有一个问题,singleton = new Singleton();这段代码底层会被分成三步。

  • 开辟空间
  • 初始化
  • 把地址赋给引用

而虚拟机底层会对这三条指令重排序,限制性哪一个不一定,这就可能会出现一个现象:
有A,B两个线程。A先进来,到达该行代码,如果先开辟空间,然后把地址赋给引用,那么singleton就不是null。此时B在最外层检查锁的位置直接返回,但是该对象并没有初始化,B再使用就会产生异常。
所以此时用到了 volatile
volatile有两个作用:

  • 保证线程可见性

  • 防止指令重排序

    这里面已经加了锁,所以只用到了第二个特性,防止指令重排序。所以最终版本是这样的:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     class Singleton {
    private volatile static Singleton singleton;
    private Singleton() {}
    public static Singleton getInstance() {
    if (singleton == null) {
    synchronized (Singleton.class) {
    if (singleton == null) {
    singleton = new Singleton();
    }
    }
    }
    return singleton;
    }
    }