单身双重锁定锁定

时间:2013-08-07 02:40:14

标签: java multithreading design-patterns 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;
        }
}

我仍然不太了解上面的代码。如果两个线程在实例为空时一起运行相同的代码行,会出现什么问题?

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

何时出现。两个线程都会看到对象为null。然后两者同步。然后,他们再次检查,仍然看到它为空。并创建两个不同的对象。糟糕!

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

谢谢:)

4 个答案:

答案 0 :(得分:63)

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

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

答案 1 :(得分:25)

让我们看看这段代码:

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类型。来自Effective Java

  

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

答案 2 :(得分:10)

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

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

Check out this link for more information.

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

答案 3 :(得分:2)

if (searchBox == null) { //1
    synchronized (SearchBox.class) {
        if (searchBox == null) {  //2
            searchBox = new SearchBox();
            }
        }
    }
}
  1. 如果已经创建了一个实例,请不要做任何事情 - 避免锁定线程
  2. 获取锁的第一个线程检查并发现没有这样的对象并创建它。它释放锁,第二个可以做同样的事情 - 它必须检查对象是否存在,因为第一个可能已创建它。
  3. 所以基本上外部if用于防止冗余锁 - 它让所有线程都知道已经存在一个对象,并且它们不需要锁定/执行任何操作。内部if用于让并发线程知道另一个线程是否已经创建了该对象。