简体   繁体   English

单例中的双重检查锁定

[英]Double Checked Locking in Singleton

here is my custom class for singleton pattern.这是我的单例模式自定义类。 in this code, I use double-checked locking as below.在这段代码中,我使用如下双重检查锁定。 As I read many posts on some source, they say that double check is useful because it prevents two concurrent threads run at same times make two different objects.当我在某些来源上阅读了许多帖子时,他们说双重检查很有用,因为它可以防止同时运行的两个并发线程生成两个不同的对象。

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

I still don't understand above code so much.我还是不太明白上面的代码。 What is the problem, if two threads together run same line of code when instance is null ?有什么问题,如果两个线程在 instance 为 null 时一起运行同一行代码?

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

When that appear.当那个出现。 both two threads will see object is null.两个线程都会看到对象为空。 then both synchronize.然后两者同步。 and then, they check again, and still see it null .然后,他们再次检查,仍然看到它 null and create two different objects.并创建两个不同的对象。 OOOPS.哎呀。

Please explain for me.请为我解释。 What have I understand wrong ?我理解错了什么?

Thanks :)谢谢 :)

No, since you are obtaining lock on the SearchBox.class , only one thread will enter the synchronized block at a time.不,由于您正在获取对SearchBox.class锁定,因此一次只有一个线程会进入同步块。 So the first thread enters then finds searchBox is null and creates it and then leaves the synchronized block, then the second thread enter the block then it finds that the searchBox is not null because the first thread already created it so it will not create a new instance of searchBox .所以第一个线程进入然后发现searchBox为空并创建它然后离开同步块,然后第二个线程进入块然后它发现searchBox不为空,因为第一个线程已经创建了它所以它不会创建一个新的searchBox实例。

The double checked pattern is used to avoid obtaining the lock every time the code is executed.双重检查模式用于避免每次执行代码时都获得锁定。 If the call are not happening together then the first condition will fail and the code execution will not execute the locking thus saving resources.如果调用没有同时发生,那么第一个条件将失败,代码执行将不会执行锁定,从而节省资源。

Let's look at this code:让我们看看这段代码:

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

Let's try to reason about this.让我们试着推理一下。 Let's say we have two threads A and B and let's assume that at least one of them reaches line 3 and observes searchBox == null is true .假设我们有两个线程AB并假设其中至少一个到达第 3 行并观察到searchBox == nulltrue Two threads can not both be at line 3 at the same time because of the synchronized block.由于synchronized块,两个线程不能同时在第 3 行。 This is the key to understanding why double-checked locking works.这是关键,理解为什么双重检查锁定的工作方式。 So, it must the case that either A or B made it through synchronized first.因此,必须是AB首先通过synchronized Without loss of generality, say that that thread is A .不失一般性,假设该线程是A Then, upon seeing searchBox == null is true, it will enter the body of the statement, and set searchBox to a new instance of SearchBox .然后,当看到searchBox == null为真时,它会进入语句的主体,并将searchBox设置为SearchBox一个新实例。 It will then eventually exit the synchronized block.然后它将最终退出synchronized块。 Now it will be B 's turn to enter: remember, B was blocked waiting for A to exit.现在轮到B进入:记住, B被阻塞等待A退出。 Now when it enters the block, it will observe searchBox .现在当它进入块时,它会观察searchBox But A will have left just having set searchBox to a non- null value.但是A将只剩下将searchBox设置为非null值。 Done.完毕。

By the way, in Java, the best way to implement a singleton is to use a single-element enum type.顺便说一下,在 Java 中,实现单例的最好方法是使用单元素enum类型。 From Effective Java :有效的Java

While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方式。

This double check lock is only necessary if you are worried about many threads calling the singleton simultaneously, or the cost of obtaining a lock in general.仅当您担心多个线程同时调用单例或一般情况下获取锁的成本时,才需要此双重检查锁。

Its purpose is to prevent unnecessary synchronization, thereby keeping your code fast in a multi-threaded environment.它的目的是防止不必要的同步,从而使您的代码在多线程环境中保持快速。

Check out this link for more information. 查看此链接了解更多信息。

If you are running in Java 1.5 or greater, and you use the volatile keyword in your double-check locked mechanism, it will work fine.如果您在 Java 1.5 或更高版本中运行,并且您在双重检查锁定机制中使用volatile关键字,它会正常工作。 As you are using the volatile keyword, your example is not broken according to the same link above.当您使用volatile关键字时,根据上面的相同链接,您的示例没有被破坏。

if (searchBox == null) { //1
    synchronized (SearchBox.class) {
        if (searchBox == null) {  //2
            searchBox = new SearchBox();
            }
        }
    }
}
  1. If an instance was already created, don't do anything - avoid locking threads如果已经创建了一个实例,不要做任何事情 - 避免锁定线程
  2. The first thread that has acquired the lock checks and sees that there is no such object and creates it.获得锁的第一个线程检查并发现没有这样的对象并创建它。 It releases the lock and the second one can do the same - it has to check if the object exists because the first one may have created it.它释放锁,第二个可以做同样的事情——它必须检查对象是否存在,因为第一个可能已经创建了它。

So basically the outer if is used to prevent redundant locks - it lets all thread know that there is already an object and they don't need to lock/do anything.所以基本上外部if用于防止冗余锁 - 它让所有线程知道已经有一个对象并且他们不需要锁定/做任何事情。 And the inner if is used to let a concurrent thread know whether another has already created the object or not.内部if用于让并发线程知道另一个线程是否已经创建了该对象。

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

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