2011-12-06 59 views
2

我在看我们的应用程序中的一些代码,我认为可能会遇到“Double-checked locking”的情况。我写了一些与我们所做的相似的示例代码。是这个密码双重锁定锁定安全吗?

任何人都可以看到这可以体验到双重检查锁定?或者这是安全的?

class Foo { 
    private Helper helper = null; 
    public Helper getHelper() { 
     Helper result; 
     synchronized(this) { 
      result = helper; 
     } 

     if (helper == null) { 
      synchronized(this) { 
       if (helper == null) { 
        helper = new Helper(); 
       } 
      } 
     } 
     return helper; 
    } 
} 

wiki借来的基本代码。

+0

是的,这是双重检查锁定 –

+0

为什么你声明'结果',然后不使用它? –

+1

@Aishwar,你可能应该更详细地阅读你链接到的维基百科页面,特别是它所说的'volatile':使用它(或不使用DCL)。 – Bruno

回答

3

双重检查锁定的关键在于快速路径(当您不需要实例化对象时)不同步。所以你所拥有的不是重复检查锁定。

你需要摆脱第一个同步块,以获得一个破损的双重检查锁。然后你需要使helper易于修复。

+0

这是最好的答案。 – bestsss

+0

谢谢。这回答了我的问题。它解释了为什么上面的代码不是双重检查锁定。 – Aishwar

6

它过于复杂,最简单的“安全”的方式做DCL就像这样:

class Foo { 
    private volatile Helper helper = null; 
    private final Object mutex = new Object(); 
    public Helper getHelper() { 
    if (helper == null) { 
     synchronized(mutex) { 
      if (helper == null) { 
       helper = new Helper(); 
      } 
     } 
    } 
    return helper; 
    } 
} 

这里的关键点是:

  • 在我们预期帮手的“高兴”的情况下已经被分配,所以如果是这样的话,我们可以直接返回它而不必输入一个同步块。
  • 帮助器被标记为易失性的,以便编译器知道可以随时从任何线程读取/写入帮助器,重要的是读/写不会重新排序。
  • 同步块使用私有最终变量进行同步以避免在this实例上同步的另一代码区域出现潜在性能冲击。
+2

您应该将帮助器分配给本地变量。 – bestsss

0

这部分是一个标准的双重检查锁定:

if (helper == null) { 
    synchronized(this) { 
     if (helper == null) { 
      helper = new Helper(); 
     } 
    } 
} 

,第一部分是一个无用的作业,没有采取任何措施的双重检查锁定部分:如果助手为null,则它也会被执行,如果这不是无论如何都不会被执行。这是完全无效的。

+0

所有其他的事情都是一样的,那是错误的。 –

+0

@Viruzzo我的理解在这里可能是错误的。但我想象这是发生了什么。在分配助手时有一个锁。并且在getHelper函数的开头还有另一个锁。因此,如果在线程2中进行分配时在线程1上调用了getHelper,则第一个同步块会保留线程1的执行,直到线程2上的分配完成。所以在设置时,helper的值永远不会被访问。 – Aishwar