双重检查锁定模式(Double-Checked Locking Pattern)是一种在多线程环境中用于减少锁开销的技术,尤其常见于实现单例模式时。其核心思想是在获取实例前进行两次检查:第一次检查是在没有同步的情况下检查实例是否已经创建,如果已创建则直接返回实例;如果没有创建,则进入第二次检查,这次检查在同步块内部执行,确保只有一个线程能够创建实例。
具体步骤如下:
- 第一次检查:线程进入getuniqueInstance ()方法时,首先检查实例(uniqueInstance )是否已经初始化过。如果是,那么直接返回实例,这样后续对该方法的调用就不需要执行下面的同步代码,从而减少了同步的开销。
- 同步检查:如果第一次检查发现实例未被初始化,这时才进入同步代码块。在同步块内部再次检查实例是否已被其他线程创建(这是“双重检查”的由来)。这一步是为了确认在进入同步块之前的短暂时间里,没有其他线程已经创建了实例。
- 创建实例:如果在同步块内部的第二次检查发现实例依然为null,那么就在该同步块内初始化实例。
这是正常的同步方法:
public class UniqueInstanceManager {
private static UniqueInstanceManager uniqueInstance = null;
private UniqueInstanceManager() {
};
public static synchronized UniqueInstanceManager getUniqueInstance() {
if (uniqueInstance == null) {
uniqueInstance = new UniqueInstanceManager();
}
return uniqueInstance;
}
}
但是这种同步方式在非静态的时候会带来额外的开销。
所以我们把sychronized加在if(uniqueInstance ==null)判断语句里面,保证uniqueInstance 未实例化的时候才加锁,减少开销。
public class UniqueInstanceManager {
private static UniqueInstanceManager uniqueInstance = null;
private UniqueInstanceManager() {}
public static UniqueInstanceManager getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (UniqueInstanceManager.class) {
if (uniqueInstance == null) {
uniqueInstance = new UniqueInstanceManager();
}
}
}
return uniqueInstance;
}
}
这种模式试图平衡资源消耗和线程安全,通过减少对同步代码块的访问次数来提高性能,同时确保线程安全。然而,这种模式在早期的Java版本中可能存在内存模型问题,导致可能初始化不完整对象的危险。Java 5及以后版本通过增加 volatile 关键字来解决了这个问题,确保了实例的可见性和禁止指令重排序,正确的双重检查锁定模式实现应该像这样:
public class UniqueInstanceManager {
private static volatile UniqueInstanceManager uniqueInstance = null;
private UniqueInstanceManager() {}
public static UniqueInstanceManager getUniqueInstance() {
if (uniqueInstance == null) {
synchronized (UniqueInstanceManager.class) {
if (uniqueInstance == null) {
uniqueInstance = new UniqueInstanceManager();
}
}
}
return uniqueInstance;
}
}
在这个修正后的版本中,uniqueInstance 变量被声明为 volatile,以确保多线程环境下的正确可见性和有序性。

被折叠的 条评论
为什么被折叠?



