Java 单例中的双重检查锁定
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/18093735/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Double Checked Locking in Singleton
提问by hqt
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. and create two different objects. OOOPS.
当那个出现。两个线程都会看到对象为空。然后两者同步。然后,他们再次检查,仍然看到它 null。并创建两个不同的对象。哎呀。
Please explain for me. What have I understand wrong ?
请为我解释。我理解错了什么?
Thanks :)
谢谢 :)
采纳答案by Arun P Johny
No, since you are obtaining lock on the SearchBox.class
, only one thread will enter the synchronized block at a time. 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.class
只有一个线程会进入同步块。所以第一个线程进入然后找到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.
双重检查模式用于避免每次执行代码时都获得锁定。如果调用没有同时发生,那么第一个条件将失败,代码执行将不会执行锁定,从而节省资源。
回答by William Morrison
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. As you are using the volatile
keyword, your example is not broken according to the same link above.
如果您在 Java 1.5 或更高版本中运行,并且您volatile
在双重检查锁定机制中使用关键字,它会正常工作。当您使用volatile
关键字时,根据上面的相同链接,您的示例没有被破坏。
回答by jason
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
. Two threads can notboth be at line 3 at the same time because of the synchronized
block. This is the keyto understanding why double-checked locking works. So, it must the case that either A
or B
made it through synchronized
first. Without loss of generality, say that that thread is 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
. It will then eventually exit the synchronized
block. Now it will be B
's turn to enter: remember, B
was blocked waiting for A
to exit. Now when it enters the block, it will observe searchBox
. But A
will have left just having set searchBox
to a non-null
value. Done.
让我们试着推理一下。假设我们有两个线程A
,B
并且假设其中至少一个到达第 3 行并观察到searchBox == null
is true
。由于阻塞,两个线程不能同时在第 3 行synchronized
。这是关键理解为什么双重检查锁定的工作方式。因此,它必须要么的情况下A
还是B
顺利过关synchronized
第一。不失一般性,假设该线程是A
. 然后,在看到searchBox == null
为真时,它将进入语句体,并设置searchBox
为 的新实例SearchBox
。然后它将最终退出该synchronized
块。现在 B
轮到输入:记住,B
被阻塞等待A
退出。现在当它进入块时,它会观察searchBox
。但是A
将离开只是设置searchBox
为非null
值。完毕。
By the way, in Java, the best way to implement a singleton is to use a single-element enum
type. From Effective Java:
顺便说一下,在 Java 中,实现单例的最好方法是使用单元素enum
类型。从有效的Java:
While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.
虽然这种方法尚未被广泛采用,但单元素枚举类型是实现单例的最佳方式。
回答by stan0
if (searchBox == null) { //1
synchronized (SearchBox.class) {
if (searchBox == null) { //2
searchBox = new SearchBox();
}
}
}
}
- If an instance was already created, don't do anything - avoid locking threads
- 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. And the inner if
is used to let a concurrent thread know whether another has already created the object or not.
所以基本上外部if
用于防止冗余锁 - 它让所有线程知道已经有一个对象并且他们不需要锁定/做任何事情。内部if
用于让并发线程知道另一个线程是否已经创建了对象。