Java通过强制同步两次来检查锁定,可行吗?

时间:2009-12-10 05:24:42

标签: java locking double-checked-locking

我已经阅读了所有关于双重检查锁定修复程序永远不会工作的内容,我不喜欢延迟初始化,但是能够修复遗留代码并且这样的问题太诱人而不试图解决会很好。

这是我的例子:     private int timesSafelyGotten = 0;     private Helper helper = null;

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

这样,同步代码必须运行一次才能创建帮助程序,并且一旦第一次运行,所以理论上,在创建帮助程序的同步代码释放锁并且必须完成帮助程序之后,理论上timesSafelyGotten不能递增初始化。

我没有看到任何问题,但它很简单似乎太好了,你觉得怎么样?

Caleb James DeLisle

3 个答案:

答案 0 :(得分:4)

没有内存屏障(synchronizedvolatile或来自java.util.concurrent的等效内容),线程可能会看到另一个线程的操作的顺序与它们在源代码中出现的顺序不同。

由于timesSafelyGotten的读取没有内存障碍,因此在分配 timesSafelyGotten之前,helper增加可能会出现另一个线程。这将导致从方法返回null

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

双重检查锁定现在正常工作,但实现正确且相当昂贵是很棘手的。延迟初始化的模式不那么脆弱,可读性更强,并且不需要任何异国情调。

答案 1 :(得分:2)

如果您使用的是JDK5 +,请使用java.util.concurrent,在您的情况下可能是AtomicInteger

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

答案 2 :(得分:1)

那不好。你可以得到timeSafelyGotten&gt; 1.示例:

  1. Thread1检查是否成功 在同步线上停止
  2. Thread2检查是否成功 停止同步代码。
  3. Thread3检查是否成功 停止同步代码。
  4. Thread1进入同步块, 创建帮助并留下这个块。
  5. Thread2落入同步块, 递增timeSafelyGotten并离开此块。
  6. Thread3落入同步块, 递增timeSafelyGotten并离开此块。
  7. 所以timeSafelyGotten = 2。

    您应该再添加一个支票:

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

    或移动同步上限:

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

    第一种方法更好,因为它不会每次都使用同步。

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

    classs MyClass {
        private Object syncRoot = new Object();
    
        ...
        synchronized(syncRoot) {
            ....
        }
    }