[英]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 }
让我们试着推理一下。 假设我们有两个线程A
和B
并假设其中至少一个到达第 3 行并观察到searchBox == null
为true
。 由于synchronized
块,两个线程不能同时在第 3 行。 这是关键,理解为什么双重检查锁定的工作方式。 因此,必须是A
或B
首先通过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();
}
}
}
}
所以基本上外部if
用于防止冗余锁 - 它让所有线程知道已经有一个对象并且他们不需要锁定/做任何事情。 内部if
用于让并发线程知道另一个线程是否已经创建了该对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.