双重locking在单身

这里是我的单身模式的自定义类。 在这段代码中,我使用了如下的双重检查locking。 当我在某些源文件上阅读了许多post时,他们说双重检查是有用的,因为它可以防止两个并发的线程同时运行两个不同的对象。

public class DoubleCheckLocking { public static class SearchBox { private static volatile SearchBox searchBox; // private attribute of this class private String searchWord = ""; private String[] list = new String[]{"Stack", "Overflow"}; // 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。 然后两个同步。 然后, 他们再次检查,仍然看到它为空 。 并创build两个不同的对象。 哎呀。

请为我解释。 我明白了什么错误?

谢谢 :)

不,因为您正在获取SearchBox.classlocking,一次只有一个线程将进入同步块。 因此,第一个线程进入,然后findsearchBox并创build它,然后离开同步块,然后第二个线程进入块,然后发现searchBox不为空,因为第一个线程已经创build它,所以它不会创build一个新的实例searchBox

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

让我们看看这个代码:

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

让我们试着对此进行推理。 假设我们有两个线程AB ,让我们假设它们中的至less一个到达第3行,并且观察searchBox == nulltrue 。 由于synchronized块,两个线程不能同时在第3行。 这是了解双重locking工作原理的关键。 所以,它必须是AB先通过synchronized 。 不失一般性,说那个线程是A 然后,在看到searchBox == null为true时,它将进入语句的主体,并将searchBox设置为SearchBox的新实例。 它将最终退出synchronized块。 现在轮到B :记住, B被阻止,等待A退出。 现在,当它进入块,它会观察searchBox 。 但是A只会将searchBox设置为非null值。 完成。

顺便说一下,在Java中,实现单例的最好方法是使用单元素enumtypes。 从有效的Java :

虽然这种方法尚未被广泛采用,但是单元枚举types是实现单例的最好方法。

这个双重检查锁只有在您担心多个线程同时调用单例时,或者一般获得锁的成本时才需要。

它的目的是防止不必要的同步,从而保持您的代码在multithreading环境中快速。

看看这个链接了解更多信息。

如果您运行的是Java 1.5或更高版本,并且在双重检查locking机制中使用volatile关键字,则它将正常工作。 由于您使用的是volatile关键字,因此您的示例不会因上面的链接而断开。

 if (searchBox == null) { //1 synchronized (SearchBox.class) { if (searchBox == null) { //2 searchBox = new SearchBox(); } } } } 
  1. 如果已经创build了一个实例,请不要做任何事情 – 避免locking线程
  2. 获得锁的第一个线程检查并看到没有这样的对象并创build它。 它释放锁,第二个可以做同样的事情 – 它必须检查对象是否存在,因为第一个可能已经创build了它。

所以基本上外层的if用于防止多余的锁 – 它让所有的线程知道已经有一个对象,他们不需要locking/做任何事情。 内部if用于让并发线程知道另一个是否已经创build了该对象。