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

  • 相关阅读:
    图解Python 【第八篇】:网络编程-进程、线程和协程
    TCP协议三次握手、四次挥手过程
    OSI七层模型与TCP/IP五层模型
    TCP/IP协议分为哪四层,具体作用是什么。
    app测试中,ios和android的区别
    APP在用户设备发生crash,应该怎么修复
    Android四层架构
    安卓四大组件、六大布局、五大存储
    测试工程师准备找工作,需要准备什么?
    接口测试响应码解析
  • 原文地址:https://www.cnblogs.com/set-cookie/p/8813619.html
Copyright © 2011-2022 走看看