精华内容
下载资源
问答
  • 接着b获得,进入synchronized代码块,也直接初始化instance,instance被初始化多遍不符合单例模式的要求~。加上第二个判空条件之后,b获得进入synchronized代码块,此时instance不为空,不执行初始化操作。 ...
    public class Singleton {
            private static volatile Singleton singleton;
    
            private Singleton() {
            }
    
            public static Singleton getInstance() {
                if (singleton == null) {                1
                    synchronized (Singleton.class) {
                        if (singleton == null) {
                            singleton = new Singleton();
                        }
                    }
                }
                return singleton;
            }
    
         
    • 第一个注意点:getInstance方法中第一个判空条件,逻辑上是可以去除的,去除之后并不影响单例的正确性,但是去除之后效率低。因为去掉之后,不管instance是否已经初始化,都会进行synchronized操作,而synchronized是一个重操作消耗性能。加上之后,如果已经初始化直接返回结果,不会进行synchronized操作。
    • 第二个注意点:加上synchronized是为了防止多个线程同时调用getInstance方法时,各初始化instance一遍的并发问题。
    • 第三个注意点:getInstance方法中的第二个判空条件是不可以去除,如果去除了,并且刚好有两个线程a和b都通过了第一个判空条件。此时假设a先获得锁,进入synchronized的代码块,初始化instance,a释放锁。接着b获得锁,进入synchronized的代码块,也直接初始化instance,instance被初始化多遍不符合单例模式的要求~。加上第二个判空条件之后,b获得锁进入synchronized的代码块,此时instance不为空,不执行初始化操作。
    • 第四个注意点:instance的声明有一个voliate关键字,如果不用该关键字,有可能会出现异常。因为instance = new Test();并不是一个原子操作,会被编译成三条指令,根据 JSR-133 定义的 JMM,synchronized 的加锁和解锁之间能够有确定的顺序,但是多个线程下 synchronized 之前的非空判断之间并不能保证顺序。所以需要用 volatile 来保证非空判断之间的 happens-before 关系。
    展开全文
  • 双重校验锁的单例模式代码如下: public class Singleton {  private static Singleton singleton;  private Singleton() {}  public static Singleton getSingleton() {  if (singleton == null) { // 1 ...

    一、结论 

    双重校验锁的单例模式代码如下:

    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
              singleton = new Singleton(); // 4
            }
          }
        }
        return singleton;
      }
    }

    假设有两个线程AB同时访问上面这段代码,它并不能保证线程安全。

    二、问题说明

      1、指令重排序的简单说明

      重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

      (1)编译器指令重排序

        编译器在不改变程序 执行结果的前提下,可以对程序的执行顺序进行优化重新排序

         (2) 处理器指令重排序

        参考:https://blog.csdn.net/javazejian/article/details/72772461 (处理器指令重排)

      2、 对象创建过程

        分为三步,如下图:

                 

    我们认为程序应该是按照1、2、3的步骤走下去,但实际上可能不是这样的,这里编译器和处理器可能会对2、3步的执行顺序进行重排序,即先将对象的引用指向内存空间,实际上A线程返回的是没有初始化的对象,然后B线程访问上面这段代码,判断if (singleton == null) { // 1 就为false,它会认为Singleton类已经实例化,问题就出在这里。

      重排序后A 、B线程执行时序图如下:

         

     三、解决方案

      1、不允许对象创建过程中2、3步发生指令重排序 (基于volatile的解决方案)

       即将Singleton声明时加上volatile,volatile关键字可以保证内存可见性和禁止指令重排序,关于volatile参见https://blog.csdn.net/javazejian/article/details/72772461 (volatile内存语义)

        修改后的代码:

        public class Singleton {
          private volatile static Singleton singleton;

          private Singleton() {}

          public static Singleton getSingleton() {
            if (singleton == null) { // 1
              synchronized (Singleton.class) { // 2
                if (singleton == null) { // 3
                  singleton = new Singleton(); // 4
                }
              }
            }
            return singleton;
          }
        }

        Singleton属性被加上volatile后,4中对象创建过程的2、3两步在多线程环境下就被禁止重排序,这样就能保证线程安全。

      2、允许对象创建过程中2、3重排序,但不允许其他线程看到这个重排序 (基于类初始化的解决方案)

        JVM在类的初始化阶段,会执行类的初始化。在执行类的初始化期间,JVM会获取一个锁,这个锁可以同步多个线程对同一个类的初始化。基于这个特性修改代码如下:

        public class Singleton {
          private static class SingletonHolder{
            public static Singleton singleton = new Singleton();
          }
          public static Singleton getSingleton(){
            return SingletonHolder.singleton;
          }
        }

            多线程访问上面这段程序的时序图如下:

       

     

     参考资料:

      1、https://blog.csdn.net/javazejian/article/details/72772461

      2、《Java并发编程的艺术》第三章 Java内存模型

        

    说明:菜鸟一枚,第一次发技术博客,如有错误或者写的不好的地方欢迎大家指正。

    转载于:https://www.cnblogs.com/-Marksman/p/9219274.html

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

    一、结论

    双重校验锁的单例模式代码如下:

    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

    singleton = new Singleton(); // 4

    }

    }

    }

    return singleton;

    }

    }

    假设有两个线程AB同时访问上面这段代码,它并不能保证线程安全。

    二、问题说明

    1、指令重排序的简单说明

    重排序是指编译器和处理器为了优化程序性能而对指令序列进行重新排序的一种手段。

    (1)编译器指令重排序

    编译器在不改变程序 执行结果的前提下,可以对程序的执行顺序进行优化重新排序

    (2) 处理器指令重排序

    参考:https://blog.csdn.net/javazejian/article/details/72772461 (处理器指令重排)

    2、 对象创建过程

    分为三步,如下图:

    b5a4cc9feb9666516e46b129f111a71b.png

    我们认为程序应该是按照1、2、3的步骤走下去,但实际上可能不是这样的,这里编译器和处理器可能会对2、3步的执行顺序进行重排序,即先将对象的引用指向内存空间,实际上A线程返回的是没有初始化的对象,然后B线程访问上面这段代码,判断if (singleton == null) { // 1 就为false,它会认为Singleton类已经实例化,问题就出在这里。

    重排序后A 、B线程执行时序图如下:

    b1a0d36af5fbaae5fd43f28d3cc2f561.png

    三、解决方案

    1、不允许对象创建过程中2、3步发生指令重排序 (基于volatile的解决方案)

    即将Singleton声明时加上volatile,volatile关键字可以保证内存可见性和禁止指令重排序,关于volatile参见https://blog.csdn.net/javazejian/article/details/72772461 (volatile内存语义)

    修改后的代码:

    public class Singleton {

    private volatile static Singleton singleton;

    private Singleton() {}

    public static Singleton getSingleton() {

    if (singleton == null) { // 1

    synchronized (Singleton.class) { // 2

    if (singleton == null) { // 3

    singleton = new Singleton(); // 4

    }

    }

    }

    return singleton;

    }

    }

    Singleton属性被加上volatile后,4中对象创建过程的2、3两步在多线程环境下就被禁止重排序,这样就能保证线程安全。

    2、允许对象创建过程中2、3重排序,但不允许其他线程看到这个重排序 (基于类初始化的解决方案)

    JVM在类的初始化阶段,会执行类的初始化。在执行类的初始化期间,JVM会获取一个锁,这个锁可以同步多个线程对同一个类的初始化。基于这个特性修改代码如下:

    public class Singleton {

    private static class SingletonHolder{

    public static Singleton singleton = new Singleton();

    }

    public static Singleton getSingleton(){

    return SingletonHolder.singleton;

    }

    }

    多线程访问上面这段程序的时序图如下:

    8f6a3545cd49cd496842b160091e8a5e.png

    参考资料:

    1、https://blog.csdn.net/javazejian/article/details/72772461

    2、《Java并发编程的艺术》第三章 Java内存模型

    说明:菜鸟一枚,第一次发技术博客,如有错误或者写的不好的地方欢迎大家指正。

    展开全文
  • 下面是小生在学习单例模式时候,对单例的优化过程。 先看几个一个单例的完善过程,过程不算漫长,正确实现必须留在最后压轴,耐心往下翻 /* 大多数单例可能写到这,但是这个实现有以下问题 1.多线程问题,多...

    写出代码不难,难的地方在于,如何写好一段代码。下面是我在学习单例模式时候,对单例的优化过程。

    先看几个一个单例的完善过程,过程不算漫长,正确实现必须留在最后压轴,耐心往下翻

     

       /*   
        大多数单例可能写到这,但是这个实现有以下问题
        1.多线程问题,多个线程可能会同事实例化多个SingletonSertvice不同对象
        */
        private SingletonService(){}
        private static SingletonService singletonService;
        public static SingletonService getInstance(){
            if (Objects.isNull(singletonService)){
                singletonService = new SingletonService();
            }
            return singletonService;
        }
    所以我们又进行了改造
    我们把getInstance()方法加上了锁,这下按说没有了线程安全的问题了吧,
     /*
        加上synchronized,保证getInstance()的线程安全性
     */
     private static SingletonService singletonService;
        public static synchronized SingletonService getInstance(){
            if (Objects.isNull(singletonService)){
                singletonService = new SingletonService();
            }
            return singletonService;
        }
    
    看上去问题是解决了,但是,性能问题又出现了,而且,如果对象已经实例化过了,现在必须得拿到锁才能判断是否已经实例化对象。时间就是生命,所以还是得优化的啊。所以现在加上双重检查锁。
     
    /*
       在getInstance中进行两次判空
    */
    private SingletonService(){}
        private static SingletonService singletonService;
        public static SingletonService getInstance(){
            if (Objects.isNull(singletonService)){
                //给SingletonService加锁
                synchronized (SingletonService.class){
                    if (Objects.isNull(singletonService)){
                        singletonService = new SingletonService();
                    }
                }
            }
            return singletonService;
        }

    But,然而,问题又来了,singletonService = new SingletonService();会出现重排序

     实例化对象的步骤是:
     1.分配内存空间
     2.初始化对象
     3.对象指向内存空间
     重排序后的步骤是:
     1.分配内存空间
     2.对象指向内存空间(此时对象不为null,但是此时并没有初始化值)
     3.初始化对象

    重排序后,可能拿到的对象并没有初始化,所以我们需要在对象上加上violatile,禁止指令重排序,做到初始化对象后,才能访问对象。所以.......

       //私有构造
        private SingletonService(){}
        //创建静态的成员变量
        private static volatile SingletonService singletonService;
        public static SingletonService getInstance(){
            /*
              双重检查锁
            */
            if (Objects.isNull(singletonService)){
                synchronized (SingletonService.class){
                    if (Objects.isNull(singletonService)){
                        singletonService = new SingletonService();
                    }
                }
            }
            return singletonService;
        }
    好了,最后完善下吧,这下应该没问题了吧,以上是我个人的简单的思路,有问题的望指教。
    展开全文
  • 一、前言单例模式(Singleton Pattern)是属于创建型模式,它提供了一种创建对象最佳方式。这种模式涉及到一个单一类,该类负责创建自己对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一对象...
  • 在网上看到过好多篇文章在说明双重检查锁在多个线程初始化一个单例类时到底为什么不行时在关键位置描述模棱两可,今天我们就来看一下为什么不能用双重检查锁,问题到底出在了那里? 下面我们直接进入主题...
  • 在这补充一点,分析下volatile是怎么在单例模式中避免双检出现问题。并发编程3个条件1、原子性:要实现原子性方式较多,可用synchronized、lock加锁,AtomicInteger等,但volatile关...
  •  * “双重检查加锁“方式可以既实现线程安全,又能够使性能不受到很大影响。  * 那么什么是”双重检查加锁“机制呢?  * 所谓双重检查加锁机制,指是:并不是每次进入getInstance方法都需要同步,  * ...
  • 单例创建模式是一个通用编程习语。和多线程一起使用时,必需使用某种类型同步。在努力创建更有效代码时,Java 程序员们创建了双重检查锁定习语,将其... Java 内存模型细节原因,并不能保证这个双重检查锁
  • 并发编程3个条件 1、原子性:要实现原子性方式较多,可用synchronized、lock加锁,AtomicInteger等,但volatile关键字是无法保证原子性; 2、可见性:要实现可见性,也可用synchronized、lock,volatile关键字...
  • 原文链接 ...在这补充一点,分析下volatile是怎么在单例模式中避免双检出现问题。 并发编程3个条件 1 原子性:要实现原子性方式较多,可用synchronized、lock加锁,AtomicInteger等,但v...
  • 在这补充一点,分析下volatile是怎么在单例模式中避免双检出现问题。并发编程3个条件1、原子性:要实现原子性方式较多,可用synchronized、lock加锁,AtomicInteger等,但volatile关...
  • 懒汉模式:在第一个线程进入2执行4之间,可能会有几个线程在2被阻塞,等第一个线程释放,这段时间线程还在,直到这些线程实例对象被创建,所以在模块里(3)再进行一次对象非空判断 public stati...
  • 双重检查锁的两层判断及synchronized的意义? 有谁能解释清楚的么?</p>
  • 使用Java实现饿、懒汉单例模式,理解其中的双重检查锁与volatile
  • 单例模式双重检查锁

    2018-10-03 15:46:53
    单例模式基本上为面试必考内容,单例模式说简单也简单,但是在面试中还是有些细节需要注意,下面提供一个懒汉的、线程安全的、避免jvm指令重排的单例书写方式,需要注意的一些点在注释中进行了说明。 ​ public ...
  • Java单例模式的双重检查锁机制 public class ClassA{ volatile private static ClassA instance = null; public static ClassA getInstance() { // 双检查锁机制,避免同步导致低并发度 if(instance ==...
  • Java中的双重检查锁(double checked locking) 在实现单例模式时,如果未考虑多线程情况,就容易写出下面错误代码: public class Singleton { private static Singleton uniqueSingleton; private ...
  • 在这补充一点,分析下volatile是怎么在单例模式中避免双检出现问题。并发编程3个条件1、原子性:要实现原子性方式较多,可用synchronized、lock加锁,AtomicInteger等,但volatile关...
  • 单例模式的检查 单例类在Java开发者中非常常用,但是它给初级开发者们造成了很多挑战。他们所面对其中一个关键挑战是,怎样确保单例类行为是单例?也就是说,无论任何原因,如何防止单例类有多个实例。在整个...
  • Java单例模式双重检查锁的问题

    万次阅读 多人点赞 2016-06-17 19:16:58
    在努力创建更有效代码时,Java 程序员们创建了双重检查锁定习语,将其和单例创建模式一起使用,从而限制同步代码量。然而,由于一些不太常见 Java 内存模型细节原因,并不能保证这个双重检查锁定习语有效。它...
  • 这里synchronized已经保证了内存可见性,以及锁的效果。主要是java的指令重排序可能导致 未被完全实例化的引用被其他线程获得。而volatile 会在读的前后加入LoadLoad屏障和LoadStore屏障,在写的前后加入StoreStore...
  • 双重检查锁实现线程安全懒汉式单例模式 public class Singleton { private volatile static Singleton uniqueInstance; private Singleton() { } public static Singleton getUniqu...
  • 解决懒汉单例模式的双重检查锁线程安全问题 答: public final class Singleton { private static Singleton singObj = null; private Singleton() { } public static Singleton getSingleInst...
  • 单例模式分为饿汉式和懒汉式。...比较稳妥办法是在懒汉式基础上加上,然后进行双重检查,这种SpringIOC容器式单例也是用这种双重检查来避免线程冲突导致问题。 public class Singleton { private volatil...
  • Java单例模式双重检查锁

    万次阅读 2018-08-01 11:38:29
    在努力创建更有效代码时,Java 程序员们创建了双重检查锁定习语,将其和单例创建模式一起使用,从而限制同步代码量。然而,由于一些不太常见 Java 内存模型细节原因,并不能保证这个双重检查...
  • 双重检查锁出现在单例模式中,单例模式分为饿汉模式和懒汉模式. 这种静态初始化方式是在自己被加载时就将自己实例化,所以被形象地称之为饿汉式单例类。 这种在第一次被引用时,才会将自己实例化,所以就被称为...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 446
精华内容 178
关键字:

双重检查锁的单例模式