基于DCL的单例懒汉模式实现
程序员文章站
2022-03-31 10:36:57
...
1:什么是DCL
全称是Double Check Lock
,即双重锁检查,是在进入同步代码块之前和之后都进行一次检查操作,之所以在进入同步代码块再检查一次的原因是,有可能在外部检查后,获取同步锁之前,当前持有锁的线程,在释放锁之前已经改变了系统状态,如下代码:
volatile int x = 1;
// 第一次检查
if (x == 1) {
// 获取同步锁
synchronize(mutexLock) {
// 第二次检查
if (x == 1) {
// 做一些操作
......
// 修改x的值为2,使不满足检查项
x = 2;
}
}
}
如下时间线:
时间 | 操作 | x的值 |
---|---|---|
T1 | 线程A成功进入同步代码块,开始操作 | 1 |
T2 | 线程B判断第一个x == 1为true,但是阻塞 | 1 |
T3 | 线程A执行x == 2修改x的值为2 | 2 |
T4 | 线程A进入同步代码块,再次判断x == 1为false,结束 | 2 |
如果是没有再次x == 1
的检查,则就产生程序错误了。
2:单例实现
2.1:代码
public class DclHungry {
private static DclHungry instance;
private static final Object mutexLock = new Object();
// 私有化构造函数,防止外部创建
private DclHungry() {}
public static DclHungry getInstance() {
// 第一次检查实例是否创建
if (instance == null) {
// 获取锁
synchronized (mutexLock) {
// 第二次检查实例是否创建
if (instance == null) {
System.out.println("实例初始化开始了");
// 创建实例
instance = new DclHungry();
System.out.println("实例初始化结束了");
}
}
}
return instance;
}
}
2.2:测试
@Test
public void testDclHungry() {
for (int i = 0; i < 3; i++) {
new Thread(() -> {
DclHungry instance = DclHungry.getInstance();
System.out.println(Thread.currentThread().getName() + "-" + instance);
}, "线程!!!" + i).start();
}
}
多次运行:
实例初始化开始了
实例初始化结束了
线程[email protected]
线程[email protected]
线程[email protected]
实例初始化开始了
实例初始化结束了
线程[email protected]
线程[email protected]
线程[email protected]
可以看到只创建了一次。我们可以将代码修改为如下,即去掉第二次检查:
public static DclHungry getInstance() {
// 第一次检查实例是否创建
if (instance == null) {
// 获取锁
synchronized (mutexLock) {
// 第二次检查实例是否创建
// if (instance == null) {
System.out.println("实例初始化开始了");
// 创建实例
instance = new DclHungry();
System.out.println("实例初始化结束了");
// }
}
}
return instance;
}
多次运行:
实例初始化开始了
实例初始化结束了
实例初始化开始了
线程[email protected]
线程[email protected]
实例初始化结束了
线程[email protected]
以上结果初始化了2次
。
实例初始化开始了
实例初始化结束了
实例初始化开始了
实例初始化结束了
实例初始化开始了
实例初始化结束了
线程[email protected]
线程[email protected]
线程[email protected]
以上结果初始化了3次
。
上一篇: SQL创建计算字段、使用函数处理数据讲解
下一篇: linux常用命令