首页 云服务器Linux编程正文

Java单例模式的双重检测

admin Linux编程 2020-05-26 11:15:48 100 0 Linux编程

1. 一样平时的单例模式如下:

class Singleton{
    private static Singleton singleton;   
    private Singleton(){}
       
    public static Singleton getInstance(){
        if(singleton == null){
            singleton = new Singleton();  // 确立实例
    }
    return singleton;
    }

问题:组织器私有使得外界无法通过组织器实例化Singleton类,要取得实例只能通过getInstance()方式。这是一个延迟加载的版本,即在需要工具的时刻才举行实例化操作。该方式在单线程下能够正常运行,然则在多线程环境下会泛起由于没有同步措施而导致发生多个单例工具的情形。

缘故原由:在于可能同时有两个线程A和B同时执行到 if 条件判断语句,A判断singleton为空准备执行(//确立实例) 时让出了CPU时间片,B也判断singleton为空,接着执行 (//确立实例),此时确立了一个实例工具;A获取了CPU时间片后接着执行(//确立实例),也确立了实例工具,这就导致多个单例工具的情形

2. 为了解决 1 中泛起的问题就是使用synchronized关键字,代码如下:

class Singleton{
    private static Singleton singleton;   
    private Singleton(){}
       
    public static synchronized Singleton getInstance(){
        if(singleton == null){
            singleton = new Singleton();    //确立实例
        }
        return singleton;
    }
}

这样解决了多线程并发的问题,然则却带来了效率问题:我们的目的是只确立一个实例,即(//确立实例)处代码只会执行一次,也正是这个地刚刚需要同步,后面确立了实例之后,singleton非空就会直接返回工具引用,而不用每次都在同步代码块中举行非空验证。

3. 为了解决 2 中泛起的问题只对(//确立实例)处举行同步:

class Singleton{
    private static Singleton singleton;   
    private Singleton(){}
   
    public static Singleton getInstance(){
        if(singleton == null){
            synchronized(Singleton.class){               
                singleton = new Singleton();  //确立实例
            }
        }
        return singleton;
    }
}

这样会带来与第一种一样的问题: 即多个线程同时执行到条件判断语句时,会确立多个实例。

缘故原由: 问题在于当一个线程确立一个实例之后,singleton就不再为空了,然则后续的线程并没有做第二次非空检查

4. 为了解决 3 中泛起的问题就是在后续的线程做第二次非空检查,代码如下:

class Singleton{
    private static Singleton singleton;   
    private Singleton(){}
   
    public static Singleton getInstance(){
        if(singleton == null){
            synchronized(Singleton.class){
                if(singleton == null)
                    singleton = new Singleton();  //确立实例
            }
        }
        return singleton;
    }
}

到这里已经很完美了,看起来没有问题。No!!!由于这种双重检测机制在jdk1.5之前是有问题的,问题照样出在(//确立实例),由所谓的无序写入造成的。

一样平时来讲,当初始化一个工具的时刻,会履历

  a. 内存分配

  b. 初始化

  c. 返回工具引用

这种方式发生的工具是一个完整的工具,可以正常使用。然则Java的无序写入可能会造成顺序的颠倒,即

  a. 内存分配、

  b. 返回工具引用

  c. 初始化的顺序,

这种情形下对应到(//确立实例)就是singleton已经不是null,而是指向了堆上的一个工具,然则该工具却还没有完成初始化动作。当后续的线程发现singleton不是null而直接使用的时刻,就会泛起意料之外的问题。

解决方案:  JDK1.5之后,可以使用volatile关键字修饰变量来解决无序写入发生的问题,由于volatile关键字的一个重要作用是制止指令重排序,即保证不会泛起内存分配、返回工具引用、初始化这样的顺序,从而使得双重检测真正发挥作用

5. 最后的双重检测单例模式为:

public class Singleton{

    private volatile static Singleton instance = null;
    private Singleton() {}

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

【版权声明】

温馨提示:文章内容系作者个人观点,不代表seo教程网对观点赞同或支持。

版权声明:本文为转载文章,来源于金邦科技 ,版权归原作者所有,欢迎分享本文,转载请保留出处!

网址:www.15544.cn


本文链接:http://15544.cn/Linuxbiancheng/65403.html

最新留言