`
zhangyeny
  • 浏览: 31637 次
  • 性别: Icon_minigender_1
  • 来自: 厦门
社区版块
存档分类
最新评论

(转)双重检查锁定失败可能性

 
阅读更多
双重检查锁定失败可能性


双重检查锁定在延迟初始化的单例模式中见得比较多(单例模式实现方式很多,这里为说明双重检查锁定问题,只选取这一种方式),先来看一个版本:

    public class Singleton { 
    
        private static Singleton instance = null; 
    
        private Singleton(){} 
    
         
    
        public static Singleton  getInstance() { 
    
           if(instance == null) { 
    
               instance = new Singleton(); 
    
           } 
    
           return instance; 
    
        } 
    
    } 

上面是最原始的模式,一眼就可以看出,在多线程环境下,可能会产生多个Singleton实例,于是有了其同步的版本:

    public class Singleton { 
    
        private static Singleton instance = null; 
    
        private Singleton(){} 
    
         
    
        public synchronized static Singleton getInstance() { 
    
           if(instance == null) { 
    
               instance = new Singleton(); 
    
           } 
    
           return instance; 
    
        } 
    
    } 

在这个版本中,每次调用getInstance都需要取得Singleton.class上的锁,然而该锁只是在开始构建Singleton 对象的时候才是必要的,后续的多线程访问,效率会降低,于是有了接下来的版本:

    public class Singleton { 
    
        private static Singleton instance = null; 
    
        private Singleton(){} 
    
         
    
        public static Singleton getInstance() { 
    
           if(instance == null) { 
    
               synchronized(Singleton.class) { 
    
                  if(instance == null) { 
    
                      instance = new Singleton(); 
    
                  } 
    
               } 
    
           } 
    
           return instance; 
    
        } 
    
    } 

很好的想法!不幸的是,该方案也未能解决问题之根本:

原因在于:初始化Singleton 和 将对象地址写到instance字段 的顺序是不确定的。在某个线程new Singleton()时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化;此时若另外一个线程来调用getInstance,取到的就是状态不正确的对象。

鉴于以上原因,有人可能提出下列解决方案:

    public class Singleton { 
    
        private static Singleton instance = null; 
    
        private Singleton(){} 
    
         
    
        public static Singleton getInstance() { 
    
           if(instance == null) { 
    
               Singleton temp; 
    
               synchronized(Singleton.class) { 
    
                  temp = instance; 
    
                  if(temp == null) { 
    
                      synchronized(Singleton.class) { 
    
                         temp = new Singleton(); 
    
                      } 
    
                      instance = temp; 
    
                  } 
    
               } 
    
           } 
    
           return instance; 
    
        } 
    
    } 

该方案将Singleton对象的构造置于最里面的同步块,这种思想是在退出该同步块时设置一个内存屏障,以阻止初始化Singleton 和 将对象地址写到instance字段 的重新排序。

不幸的是,这种想法也是错误的,同步的规则不是这样的。退出监视器(退出同步)的规则是:所以在退出监视器前面的动作都必须在释放监视器之前完成。然而,并没有规定说退出监视器之后的动作不能放到退出监视器之前完成。也就是说同步块里的代码必须在退出同步时完成,而同步块后面的代码则可以被编译器或运行时环境移到同步块中执行。

编译器可以合法的,也是合理的,将instance = temp移动到最里层的同步块内,这样就出现了上个版本同样的问题。

在JDK1.5及其后续版本中,扩充了volatile语义,系统将不允许对 写入一个volatile变量的操作与其之前的任何读写操作 重新排序,也不允许将 读取一个volatile变量的操作与其之后的任何读写操作 重新排序。

在jdk1.5及其后的版本中,可以将instance 设置成volatile以让双重检查锁定生效,如下:

    public class Singleton { 
    
        private static volatile Singleton instance = null; 
    
        private Singleton(){} 
    
         
    
        public static Singleton getInstance() { 
    
           if(instance == null) { 
    
               synchronized(Singleton.class) { 
    
                  if(instance == null) { 
    
                      instance = new Singleton(); 
    
                  } 
    
               } 
    
           } 
    
           return instance; 
    
        } 
    
    } 

需要注意的是:在JDK1.4以及之前的版本中,该方式仍然有问题。
分享到:
评论

相关推荐

    c#.net学习基础

    双重检查锁定(Double-Check Locking)是一种优化同步的策略,用于确保单例模式的安全初始化。在给出的示例中,`LockTest`方法中的`lock`语句实现了线程安全,但双重检查缺失。 OOP(面向对象编程)强调对象的封装...

    only-run-one-application.rar_Only

    6. **.NET Framework的Singleton模式**:在C#等.NET语言中,可以通过静态成员和私有构造函数来实现单例,例如`Lazy<T>`类或双重检查锁定(Double-Check Locking)模式。 实现这样的功能可以避免用户意外打开多个...

    JNDI连接池+单例模式+文件上传

    实现单例模式的方法有很多,如懒汉式、饿汉式、双重检查锁定等。以下是一个简单的双重检查锁定实现: ```java public class DataSourceSingleton { private static volatile DataSource instance; private ...

    图书管理系统的源代码

    通过双重检查锁定(double-checked locking)实现线程安全的初始化,避免并发环境下多次创建连接。 - 如果连接为null,`Dao`构造函数会尝试加载JDBC驱动并建立连接,确保在后续操作中能正常访问数据库。 3. 错误...

    深入理解JAVA内存模型.pdf

    - 理解和避免并发陷阱,如双重检查锁定、线程中断、线程通信等。 - 了解并使用并发工具类,如ConcurrentHashMap、CountDownLatch、CyclicBarrier等。 总之,深入理解Java内存模型有助于我们编写出更加健壮、高效...

    高级面试题

    14. 单例模式的懒汉模式在多线程环境下不是线程安全的,可以采用双重检查锁定(DCL)或静态内部类的方式来实现线程安全的懒汉模式。 综上所述,掌握Java面试题中的这些知识点对于Java开发者来说至关重要,这些都是...

    java高并发编程源码.zip

    - 双重检查锁定模式 (Double-Check Locking) 通过分析`hakesashou`这个文件名,可能是一个名为“筷子手”(谐音)的并发工具或框架,具体功能需要查看源码来了解。学习和理解这些Java高并发编程的知识点,将有助于...

    Windows系统还原点不起作用若干原因.docx

    - 在还原过程中,如果“系统还原”要替换、移动或删除的文件被其他程序锁定,还原过程可能会失败。 - 这种情况在Windows XP Service Pack 1之前较为常见。 **2. 解决方案** - 关闭所有可能锁定文件的应用程序。 - ...

    一本经典的多线程书籍 Java并发编程 设计原则与模式 第二版 (英文原版)

    - 双重检查锁定(Double-Check Locking):优化单例模式,防止不必要的同步。 5. **并发编程实践** - 线程池的最佳实践:根据任务性质和系统资源动态调整线程池大小。 - 异常处理:在并发环境中,异常可能导致...

    基于C#的应用程序单例唯一运行的完美解决方案

    Instance属性使用双重检查锁定(Double-Check Locking)模式来创建单例,同时在创建实例的同时初始化Mutex。Mutex的命名使用了全局命名空间("Global\\..."),这样不同进程间的Mutex也能识别到彼此。如果Mutex创建...

    安卓手势解锁 两次手势加锁,解锁需验证

    在此基础上,这个特殊实现增加了一个双重验证的概念,即用户需要连续两次绘制相同的手势才能成功锁定或解锁设备。这提高了解锁的安全性,防止了误操作或者他人尝试解锁时的偶然匹配。 在实现这个功能时,开发人员...

    博文《C#中只启动一个程序实例》中源代码(修订改进版)

    这种实现方式被称为"双检锁/双重检查锁定"(Double-Checked Locking)模式,它兼顾了性能和线程安全性。 然而,仅仅实现单例还不足以确保程序只有一个实例在运行。为了阻止用户启动第二个实例,我们需要监听操作...

    故障安全

    在多线程环境中,为了实现故障安全的单例,可以采用双重检查锁定(Double-Check Locking)策略,防止多个线程同时创建实例,确保线程安全。 5. 防止空指针异常(NullPointerException): 在访问对象的属性或方法...

    java多线程设计模式

    Java中实现线程安全的单例有多种方法,比如双重检查锁定(Double-Checked Locking)、静态内部类、枚举等。 4. 管道模式:线程间通信的一种方式,通过管道可以将一个线程的输出作为另一个线程的输入。Java的`...

    多线程设计模式

    Java中常见的实现方式有双重检查锁定(DCL)、静态内部类和枚举等。 3. 管道线程模式:线程间通过管道进行通信,例如使用PipedInputStream和PipedOutputStream,用于线程间的同步和数据传递。 4. 线程池模式:通过...

    并发编程笔记+包含所有

    3. 双重检查锁定(Double-Checked Locking):确保单例模式在多线程环境下的正确初始化。 六、并发编程注意事项 1. 避免过度同步:过多的同步可能导致性能下降,应尽量减少同步代码块的范围。 2. 死锁:两个或多个...

    信息系统安全策略分析.pdf

    频繁失败的登录尝试会触发锁定机制。系统管理员密码的变更和管理也受到严格控制,以确保责任明确。 用户注册和注销流程规范化,权限申请需经过审批。超级权限的分配基于具体任务需求,只赋予必要的人员,确保最小...

    中兴通java程序员面试题(高)

    15. **单例模式实现**:通常使用双重检查锁定(Double-Checked Locking)或静态内部类方式实现。 16. **J2EE设计模式**:常见的有工厂模式(用于对象创建),单例模式(保证类只有一个实例),观察者模式(对象间一...

    美团java视频一面1

    - 通常使用单例模式,例如双重检查锁定(DCL)或静态内部类方式,直接设置`maximumPoolSize`为1不是最佳实践。 9. **高并发解决方案** - 可以使用同步机制(如`synchronized`关键字、锁)、线程池、异步处理、...

    C#面向对象设计模式纵横谈(2):Singleton 单件(创建型模式) (Level 300)

    4. **双重检查锁定(DCL,线程安全)** 结合了懒汉式的延迟加载和饿汉式的线程安全性,只有在第一次访问时才进行实例化。 ```csharp public class Singleton { private static volatile Singleton instance; ...

Global site tag (gtag.js) - Google Analytics