singleton 类_在Java中对Singleton类进行双重检查锁定

singleton 类

Singleton类在Java开发人员中非常常见,但是它给初级开发人员带来了许多挑战。 他们面临的主要挑战之一是如何使Singleton保持为Singleton? 也就是说,无论出于何种原因,如何防止单个实例的多个实例。 对Singleton进行双重检查锁定是一种确保在应用程序生命周期中仅创建Singleton类的一个实例的方法。 顾名思义,在两次检查锁定中,代码对一次Singleton类的现有实例进行两次检查(不进行双重锁定),以确保不会创建一个以上的singleton实例。 顺便说一句,它在Java修复JDK 1.5中的内存模型问题之前就被破坏了。 在本文中,我们将看到如何为Java中的Singleton的双重检查锁定编写代码 ,为什么在Java 5之前双重检查锁定被破坏以及如何解决。 顺便说一句,从访谈的角度来看,这也很重要,我听说有人要求对金融和服务业的公司进行手工双重检查Singleton锁定的代码,并相信我很棘手,直到您清楚地了解了什么你在做。 您还可以查看我的Singleton设计模式问题的完整列表,以进行良好的准备。

Singleton类破坏其合同的一种常见情况是多线程。 如果您要求初学者为Singleton设计模式编写代码,那么他很有可能会提出以下内容:

private static Singleton _instance;

public static Singleton getInstance() {
        if (_instance == null) {
            _instance = new Singleton();
        }
        return _instance;
}

并且当您指出这段代码将由多个线程并行调用时,将创建Singleton类的多个实例时,他可能会使整个getInstance()方法同步化 ,如第二个代码示例getInstanceTS()方法所示。 尽管它是线程安全的,并且可以解决多个实例的问题,但效率不是很高。 每次调用此方法时,都需要承担同步的费用,而创建Singleton实例时,仅在第一类上才需要同步。 这将使我们进入双重检查的锁定模式 ,即只有关键的代码段被锁定。 程序员称其为“双重检查锁定”,因为对_instance == null进行了两次检查,一次没有锁定,而另一次则带有锁定(内部同步)块。 这是Java中经过双重检查的锁定的样子:

public static Singleton getInstanceDC() {
        if (_instance == null) {                // Single Checked
            synchronized (Singleton.class) {
                if (_instance == null) {        // Double checked
                    _instance = new Singleton();
                }
            }
        }
        return _instance;
}

单例设计模式
从表面上看,这种方法看起来很完美,因为您只需要为同步块支付一次费用,但是直到使_instance变量为volatile时,它还是坏了。 如果没有volatile修饰符,则Java中的另一个线程可能会看到_instance变量的一半初始化状态,但是由于volatile变量保证了before-before关系的发生,所有写操作都会在_instance变量的任何读取之前发生在volatile _instance上。 在Java 5之前不是这种情况,这就是为什么以前双重检查锁定已被破坏的原因。 现在,有了事前保证 ,您可以放心地认为这将起作用。 顺便说一下,这不是创建线程安全的Singleton的最佳方法,可以将Enum用作Singleton ,它在实例创建过程中提供内置的线程安全性。 另一种方法是使用静态持有人模式。

/*
 * A journey to write double checked locking of Singleton class in Java.
 */

class Singleton {

    private volatile static Singleton _instance;

    private Singleton() {
        // preventing Singleton object instantiation from outside
    }

    /*
     * 1st version: creates multiple instance if two thread access
     * this method simultaneously
     */

    public static Singleton getInstance() {
        if (_instance == null) {
            _instance = new Singleton();
        }
        return _instance;
    }

    /*
     * 2nd version : this definitely thread-safe and only
     * creates one instance of Singleton on concurrent environment
     * but unnecessarily expensive due to cost of synchronization
     * at every call.
     */

    public static synchronized Singleton getInstanceTS() {
        if (_instance == null) {
            _instance = new Singleton();
        }
        return _instance;
    }

    /*
     * 3rd version : An implementation of double checked locking of Singleton.
     * Intention is to minimize cost of synchronization and  improve performance,
     * by only locking critical section of code, the code which creates
 instance of Singleton class.
     * By the way this is still broken, if we don't make _instance volatile,
 as another thread can
     * see a half initialized instance of Singleton.
     */

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

这就是有关Java中Singleton类的双重检查锁定的全部内容。 这是在Java中创建线程安全的Singleton的有争议的方法之一,就将Enum用作Singleton类而言,还有其他更简单的选择。 我不建议您像这样实现Singleton,因为有许多更好的方法可以在Java中实现Singleton模式。 虽然,这个问题具有历史意义,并且还教导了并发如何引入细微的错误。 正如我之前所说,从访谈的角度来看,这非常重要。 在进行任何Java面试之前,练习手工编写Singleton类的双重检查锁定。 这将使您深入了解Java程序员的编码错误。 与此相关的是,在现代的测试驱动开发中,由于Singleton难以模拟其行为,因此Singleton被视为反模式,因此,如果您是TDD实践者,最好避免使用Singleton模式。

翻译自: https://www.javacodegeeks.com/2014/05/double-checked-locking-on-singleton-class-in-java.html

singleton 类

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值