繁体   English   中英

单例中的双重检查锁定

[英]Double Checked Locking in Singleton

这是我的单例模式自定义类。 在这段代码中,我使用如下双重检查锁定。 当我在某些来源上阅读了许多帖子时,他们说双重检查很有用,因为它可以防止同时运行的两个并发线程生成两个不同的对象。

public class DoubleCheckLocking {

    public static class SearchBox {
        private static volatile SearchBox searchBox;

        // private constructor
        private SearchBox() {}

        // static method to get instance
        public static SearchBox getInstance() {
            if (searchBox == null) { // first time lock
                synchronized (SearchBox.class) {
                    if (searchBox == null) {  // second time lock
                        searchBox = new SearchBox();
                    }
                }
            }
            return searchBox;
        }
}

我还是不太明白上面的代码。 有什么问题,如果两个线程在 instance 为 null 时一起运行同一行代码?

if (searchBox == null) {
                synchronized (SearchBox.class) {
                    if (searchBox == null) {
                        searchBox = new SearchBox();
                    }
                }
            }

当那个出现。 两个线程都会看到对象为空。 然后两者同步。 然后,他们再次检查,仍然看到它 null 并创建两个不同的对象。 哎呀。

请为我解释。 我理解错了什么?

谢谢 :)

不,由于您正在获取对SearchBox.class锁定,因此一次只有一个线程会进入同步块。 所以第一个线程进入然后发现searchBox为空并创建它然后离开同步块,然后第二个线程进入块然后它发现searchBox不为空,因为第一个线程已经创建了它所以它不会创建一个新的searchBox实例。

双重检查模式用于避免每次执行代码时都获得锁定。 如果调用没有同时发生,那么第一个条件将失败,代码执行将不会执行锁定,从而节省资源。

让我们看看这段代码:

1 if (searchBox == null) {
2     synchronized (SearchBox.class) {
3     if (searchBox == null) {
4         searchBox = new SearchBox();
5     }
6 }

让我们试着推理一下。 假设我们有两个线程AB并假设其中至少一个到达第 3 行并观察到searchBox == nulltrue 由于synchronized块,两个线程不能同时在第 3 行。 这是关键,理解为什么双重检查锁定的工作方式。 因此,必须是AB首先通过synchronized 不失一般性,假设该线程是A 然后,当看到searchBox == null为真时,它会进入语句的主体,并将searchBox设置为SearchBox一个新实例。 然后它将最终退出synchronized块。 现在轮到B进入:记住, B被阻塞等待A退出。 现在当它进入块时,它会观察searchBox 但是A将只剩下将searchBox设置为非null值。 完毕。

顺便说一下,在 Java 中,实现单例的最好方法是使用单元素enum类型。 有效的Java

虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方式。

仅当您担心多个线程同时调用单例或一般情况下获取锁的成本时,才需要此双重检查锁。

它的目的是防止不必要的同步,从而使您的代码在多线程环境中保持快速。

查看此链接了解更多信息。

如果您在 Java 1.5 或更高版本中运行,并且您在双重检查锁定机制中使用volatile关键字,它会正常工作。 当您使用volatile关键字时,根据上面的相同链接,您的示例没有被破坏。

if (searchBox == null) { //1
    synchronized (SearchBox.class) {
        if (searchBox == null) {  //2
            searchBox = new SearchBox();
            }
        }
    }
}
  1. 如果已经创建了一个实例,不要做任何事情 - 避免锁定线程
  2. 获得锁的第一个线程检查并发现没有这样的对象并创建它。 它释放锁,第二个可以做同样的事情——它必须检查对象是否存在,因为第一个可能已经创建了它。

所以基本上外部if用于防止冗余锁 - 它让所有线程知道已经有一个对象并且他们不需要锁定/做任何事情。 内部if用于让并发线程知道另一个线程是否已经创建了该对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM