单例模式之双重检测锁

  先来看看双重检测锁的实现以及一些简要的说明(本文主要说明双重检测锁带来的线程安全问题):

  

/**
 * 单例模式之双检锁
 * @author ring2
 * 懒汉式升级版
 */
public class Singleton3 {

    private static volatile Singleton3 instance;

    private Singleton3() {}

    public static Singleton3 getInstance() {

        //首先判断是否为空
        if(instance==null) {
            //可能多个线程同时进入到这一步进行阻塞等待
            synchronized(Singleton3.class) {
                //第一个线程拿到锁,判断不为空进入下一步
                if(instance==null) {
                    /**
                     * 由于编译器的优化、JVM的优化、操作系统处理器的优化,可能会导致指令重排(happen-before规则下的指令重排,执行结果不变,指令顺序优化排列)
                     * new Singleton3()这条语句大致会有这三个步骤:
                     * 1.在堆中开辟对象所需空间,分配内存地址
                     * 2.根据类加载的初始化顺序进行初始化
                     * 3.将内存地址返回给栈中的引用变量
                     *
                     * 但是由于指令重排的出现,这三条指令执行顺序会被打乱,可能导致3的顺序和2调换
                     * ??
                     */
                    instance = new Singleton3();
                }
            }
        }
        return instance;
    }
}

  由于指令重排导致3,2的顺序调换以及处于多线程场景,会导致以下问题的出现首先第一个线程执行到了3号指令(instance变量被分配了地址,不为null了),但对象未初始化。此时!第一个或者第二个if语句进行判断时结果为true,自然而然在使用instance时会出错。

  解决的方法便是在instance变量上加上volatile关键字,加上volatile关键字后会禁止该变量的指令重排,从而达到线程安全。关于volatile关键字。。。又可以说上一篇。在其他篇章。

原文地址:https://www.cnblogs.com/ring2/p/11401809.html

时间: 2024-11-02 01:57:37

单例模式之双重检测锁的相关文章

单例模式和双重检测的小结

设计模式的经典模式之一--单例模式 这个也是面试经常被问起的面试,一般都要求手写 [饿汉模式]非延时加载 --以空间换时间 [懒汉模式]延时加载--以时间换空间 看似简单,但是在懒汉模式还隐藏了一个双重检测,其中还是涉及到JVM内存的"无序写入"问题(后面使用的 volatile ) 1.饿汉模式 比较简单,非延时加载,一上来就把对象给new出来了 <span style="font-family:Microsoft YaHei;font-size:14px;"

线程安全的单例模式及双重检查锁—个人理解

在web应用中服务器面临的是大量的访问请求,免不了多线程程序,但是有时候,我们希望在多线程应用中的某一个类只能新建一个对象的时候,就会遇到问题. 首先考虑单线程,如果要求只能新建一个对象,那么构造函数我们要设为private.简单的想法: class singleton{ private singleton(){ //..... } private static singleton instance; public static singleton getinstance(){ if(insta

单例模式的双重检测

单例模式是设计模式中比较常见简单的一种,典型双重检测写法如下: public class SingletonClass { private volatile static SingletonClass instance = null; public static SingletonClass getInstance() { if (instance == null) { synchronized (SingletonClass.class) { if(instance == null) { in

Java单例模式中双重检查锁的问题

单例创建模式是一个通用的编程习语.和多线程一起使用时,必需使用某种类型的同步.在努力创建更有效的代码时,Java 程序员们创建了双重检查锁定习语,将其和单例创建模式一起使用,从而限制同步代码量.然而,由于一些不太常见的 Java 内存模型细节的原因,并不能保证这个双重检查锁定习语有效. 它偶尔会失败,而不是总失败.此外,它失败的原因并不明显,还包含 Java 内存模型的一些隐秘细节.这些事实将导致代码失败,原因是双重检查锁定难于跟踪.在本文余下的部分里,我们将详细介绍双重检查锁定习语,从而理解它

Java并发笔记——单例与双重检测

单例模式可以使得一个类只有一个对象实例,能够减少频繁创建对象的时间和空间开销.单线程模式下一个典型的单例模式代码如下: ① 1 class Singleton{ 2 private static Singleton singleton; 3 private Singleton(){} 4 5 public static Singleton getInstance(){ 6 if(singleton == null){ 7 singleton = new Singleton(); //1 8 }

关于并发场景下,通过双重检查锁实现延迟初始化的优化问题隐患的记录

首先,这个问题是从<阿里巴巴Java开发手册>的1.6.12(P31)上面看到的,里面有这样一句话,并列出一种反例代码(以下为仿写,并非与书上一致): 在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优化问题隐患,推荐解决方案中较为简单的一种(适用于JDK5及以上的版本),即目标属性声明为volatile型. 1 public class Singleton { 2 private static Singleton instance=null; 3

单例模式中用volatile和synchronized来满足双重检查锁机制

背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1:没有volatile修饰的uniqueInstance public class Singleton { private static Singleton uniqueInstance; private Singleton(){ } public static Singleton getInsta

关于双重检验锁方法和内部类方法实现单例模式的实验

网上很多人说,使用双重检验锁方法实现单例模式可能会new多个实例,而内部类方法和枚举类方法完美解决了这个问题 因为Android很少使用枚举,本次只研究双重检验锁方法和内部类方法 双重检验锁方法: 代码如下: public class SingletonB { private static volatile SingletonB mInstance; private SingletonB() { System.out.println("双重检验锁方法单例模式"); } public s

双重检查锁实现单例模式的线程安全问题

一.结论 双重校验锁的单例模式代码如下: public class Singleton { private static Singleton singleton; private Singleton() {} public static Singleton getSingleton() { if (singleton == null) { // 1 synchronized (Singleton.class) { // 2 if (singleton == null) { // 3 single