zoukankan      html  css  js  c++  java
  • 双重检验锁思考

    最近在项目中写一个池子,用到了双重检验锁,联想到单例模式的双重检验锁。

    1 单例模式

    下面是一个懒加载的单例模式

    public class Singleton {
        private volatile Singleton instance = null; // volatile禁止指令重排序
    
        private Singleton() {
    
        }
    
        public static Singleton getInstance() {
            if (instance == null) { // 1. 减少锁粒度,避免不必要的加锁
                synchronized (Singleton.class) {
                    if (instance == null) { //  2. 获取锁后要再次检验状态,以为状态可能被其他线程改变
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    需要判断两次instace == null是因为

    1. 第一次为了减少锁了粒度,因为只有在instance == null的时候才需要上锁,其他情况可以直接返回
    2. 第二次获取锁后还需要判断 instance == null是因为instance可能已经被改变,所以要再次判断。例如:两个线程都在等待获取锁。线程A获取到后实例化了instance后释放了锁,instance现在不是null;之后线程B获取到锁,如果不判断instance == null的话便会又重新创建一个instance。

    其实加锁的原因是判断null和new对象需要是个原子操作,而在锁之外判断null的原因是减少锁的粒度。

    因此我们可以总结对于类似情况的锁使用

    • 在需要的时候才上锁,减少锁粒度,减少锁竞争
    • 获取锁后要判断状态,因为其他线程可能会改变状态

    2 volatile

    另外注意到,类的属性使用了volatile。先写下结论:这里的volatile是为了禁止重排序,而不是可见性。

    原因是:new一个对象其实不是原子的,需要3个步骤

    1. 给 instance 分配内存
    2. 调用 Singleton 的构造函数来初始化成员变量
    3. 将instance对象指向分配的内存空间(执行完这步 instance 就为非 null 了)

    jvm存在指令重排序优化,有可能上述步骤变为1-3-2。假设现在的执行顺序是1-3-2,现在有两个线程A和B。A获取锁后,执行new对象,执行步骤是1-3,还未执行2。需要注意的是,执行完3后jinstance就未非null了,而第2步还没有执行,对象不是完整的对象,此时如果判断instance == null 将返回false。此时线程B执行判断同步代码块外的 instance == null 判断(即1. 减少锁粒度,避免不必要的加锁 这个判断),得到结果false,直接返回了这个不完整的对象。因此这里volatile是为了避免指令重排序,而不是可见性。

    3 参考

    http://wuchong.me/blog/2014/08/28/how-to-correctly-write-singleton-pattern/
    https://blog.csdn.net/xiakepan/article/details/52444565

  • 相关阅读:
    The Python Standard Library
    Python 中的round函数
    Python文件类型
    Python中import的用法
    Python Symbols 各种符号
    python 一行写多个语句
    免费SSL证书(https网站)申请,便宜SSL https证书申请
    元宇宙游戏Axie龙头axs分析
    OLE DB provider "SQLNCLI10" for linked server "x.x.x.x" returned message "No transaction is active.".
    The operation could not be performed because OLE DB provider "SQLNCLI10" for linked server "xxx.xxx.xxx.xxx" was unable to begin a distributed transaction.
  • 原文地址:https://www.cnblogs.com/set-cookie/p/8813619.html
Copyright © 2011-2022 走看看