随笔 - 171  文章 - 0  评论 - 0  阅读 - 62249

double check lock

双重检查(单例模式)

复制代码
class Singleton{
    private volatile static Singleton instance = null;
    private Singleton() {
    }
    public static Singleton getInstance() {
        if(instance==null) { // 1
            synchronized (Singleton.class) {
                if(instance==null)
                    instance new Singleton();  //2
            }
        }
        return instance;
    }
}
复制代码

在双重检查锁模式中为什么需要使用 volatile 关键字?

假如 Instance 类变量是没有用 volatile 关键字修饰的,会导致这样一个问题:
在线程执行到第 1 行的时候,代码读取到 instance 不为 null 时,instance 引用的对象有可能还没有完成初始化。
造成这种现象主要的原因是创建对象不是原子操作以及指令重排序。
第二行代码可以分解成以下几步:
memory = allocate();  // 1:分配对象的内存空间
ctorInstance(memory); // 2:初始化对象
instance = memory;  // 3:设置instance指向刚分配的内存地址
根源在于代码中的 23 之间,可能会被重排序。例如:
memory = allocate();  // 1:分配对象的内存空间
instance = memory;  // 3:设置instance指向刚分配的内存地址
// 注意,此时对象还没有被初始化!
ctorInstance(memory); // 2:初始化对象
这种重排序可能就会导致一个线程拿到的 instance 是非空的但是还没初始化完全。

为什么要 double-check?去掉任何一次的 check 行不行?

我们先来看第二次的 check,这时你需要考虑这样一种情况,有两个线程同时调用 getInstance 方法,由于 singleton 是空的 ,因此两个线程都可以通过第一重的 if 判断;然后由于锁机制的存在,会有一个线程先进入同步语句,并进入第二重 if 判断 ,而另外的一个线程就会在外面等待。
不过,当第一个线程执行完 new Singleton() 语句后,就会退出 synchronized 保护的区域,这时如果没有第二重 if (singleton == null) 判断的话,那么第二个线程也会创建一个实例,此时就破坏了单例,这肯定是不行的。
而对于第一个 check 而言,如果去掉它,那么所有线程都会串行执行,效率低下,所以两个 check 都是需要保留的。
posted on   zhengbiyu  阅读(26)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示