2009-12-10 56 views
3

我读过所有关于双重检查锁定修复程序如何工作的内容,我不喜欢延迟初始化,但能够修复遗留代码并且这样的问题太诱人而不尝试解决。我的例子: private int timesSafelyGotten = 0; private Helper helper = null;Java通过强制同步两次检查加锁,可行吗?

public getHelper() 
{ 
    if (timesSafelyGotten < 1) { 
     synchronized (this) { 
      if (helper == null) { 
       helper = new Helper(); 
      } else { 
       timesSafelyGotten++; 
      } 
     } 
    } 
    return helper; 
} 

这样,同步代码必须运行一次创建帮助,一旦当它得到首次所以理论上timesSafelyGotten不能递增,直到它创造的助手同步码后已经释放锁定和助手必须完成初始化。

我没有看到任何问题,但它是如此简单,似乎太好,不真实,你怎么看?

迦勒詹姆斯·迪莱尔

+6

如果您阅读(并了解)为什么双重检查锁定被破坏的详细解释之一,您将明白为什么您的代码也被破坏。 – 2009-12-10 05:33:34

+1

它也值得注意的是,随着Java 5和易失性,你可以做不间断的双重检查锁定。 Java 4和更早的你的声明确实如此。 – 2009-12-10 05:40:36

+2

还值得注意的是,使用J5并大大提高了无争用锁的性能,DCL几乎总是过早的优化,不值得冒这个风险。 – 2009-12-10 06:16:53

回答

2

如果使用JDK5 +,使用java.util.concurrent中,你的情况可能AtomicInteger

这些实用程序是专门提供的,因为没有人可以理解低级别的线程同步原语足以使它们正常工作。

1

这并不好。就可以得到timeSafelyGotten> 1。实施例:

  1. 线程1检查是否成功地和同步线 停止
  2. 线程2检查是否在同步代码成功和 停止。
  3. 线程3检查是否成功,并且 停止在同步代码上。
  4. 线程1落入同步块, 创建助手并离开此块。
  5. 线程2落入同步块, 递增timeSafelyGotten并离开此块。
  6. 线程3落入同步块, 递增timeSafelyGotten并离开此块。

所以timeSafelyGotten = 2

你应该增加一个检查:

if (helper == null) { 
    helper = new Helper(); 
} else if (timesSafelyGotten < 1) { 
    timesSafelyGotten++; 
} 

或移动同步上:

synchronized(this) { 
    if (timeSafelyGotten < 1) { 
     ... 
    } 
} 

第一种方式是更好,因为它不”每次使用同步。

还有一个提示:不要使用同步(这个),因为有人也可以使用你的对象进行同步。使用特殊的私有对象为内部同步:

classs MyClass { 
    private Object syncRoot = new Object(); 

    ... 
    synchronized(syncRoot) { 
     .... 
    } 
} 
+0

感谢您的快速回复,不是线程2和3被阻止在同步代码之外(在步骤5),直到助手完成初始化?如果是这样,那么它的工作不是吗?我也喜欢同步(syncRoot)的想法。 – 2009-12-10 06:34:26

+0

是的,他们被阻止,但他们在第一次检查后被阻止,并在解除阻止后陷入同步块。我已经重写了这个例子,使之更加清晰。 – 2009-12-10 09:03:57

4

在没有存储器屏障(​​,volatile,或从java.util.concurrent当量),线程可看到另一个线程的动作发生在不同的顺序出现在源代码。

由于有上timesSafelyGotten读取无记忆障碍,它可以出现另一个线程timesSafelyGotten递增之前helper分配。这将导致从该方法返回null

实际上,在单元测试期间,这可能适用于许多架构。但它不正确,最终会失败。

双重锁定确实现在正在工作,但实现正确和相当昂贵的技术很棘手。有一些延迟初始化模式不易脆弱,可读性更强,而且不需要任何特殊的东西。

+0

您是否有链接到最新版本的Java中的惰性初始化模式的优秀教程? – 2009-12-10 07:25:11

+0

查看“初始化需求持有者”习惯用法:http://en.wikipedia.org/wiki/Initialization_on_demand_holder_idiom – erickson 2009-12-10 07:51:17

+0

我也喜欢IODH - 简单,懒惰的初始化,快速且没有同步问题。 – blackanchorage 2009-12-10 08:28:21