单例中的双重检查锁定

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

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;
        }
}

我仍然不太理解上面的代码。如果两个线程一起运行同一行代码,而实例为 null,这是什么问题?

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

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

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

谢谢:)


答案 1

否,由于您正在获取 上的锁定,因此一次只有一个线程将进入同步块。因此,第一个线程进入,然后找到是空并创建它,然后离开同步的块,然后第二个线程进入该块,然后它发现不是空的,因为第一个线程已经创建了它,因此它不会创建一个新的实例。SearchBox.classsearchBoxsearchBoxsearchBox

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


答案 2

让我们看一下这段代码:

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

让我们试着对此进行推理。假设我们有两个线程,并且假设其中至少有一个到达第 3 行并观察到是 。由于阻塞,两个线程不能同时位于第 3 行。这是理解为什么双重检查锁定有效的关键。因此,它必须首先通过或通过的情况。在不失去通用性的情况下,假设该线程是 。然后,在看到为 true 时,它将进入语句的正文,并设置为 的新实例 。然后,它最终将退出该块。现在轮到进入了:记住,被阻止等待退出。现在,当它进入块时,它将观察.但将只剩下设置为非值。做。ABsearchBox == nulltruesynchronizedABsynchronizedAsearchBox == nullsearchBoxSearchBoxsynchronizedBBAsearchBoxAsearchBoxnull

顺便说一句,在Java中,实现单例的最佳方法是使用单元素类型。来自 Effective Javaenum

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


推荐