Java单例模式代码实现和DCL和volatile详细解析
十月 29, 2020
网上搜一下单例模式发现都没有说的很全面的,所以总结一下
饿汉式
饿汉式没有线程安全问题,在类加载时就会初始化一个对象,每次 getInstance()得到的也是原来的对象。
1 | class Singleton { |
懒汉式
懒汉式才是重点,由于存在线程安全问题,所以懒汉式的实现有更多的细节。
循序渐进来看一下,懒汉式的变化。
初始版本
1 | class Singleton { |
最开始版本在多线程环境下是不安全的,可能会同时new很多次Singleton对象。
直接在方法上加锁
最直接的方法是在方法上直接加上synchronized。
1 | class Singleton { |
但是这么做锁的粒度太大,有更加美妙的方案。
细化锁的粒度
把锁加在方法内部。这里就是DCL(DoubleCheckLock)。
1 | class 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
14class 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;
}
}
查看评论