5

此代码是否解决了 Java 中的双重检查锁定问题?

public class DBAccessService() {
    private static DBAccessService INSTANCE;  

    private DBAccessService() {}

    public static DBAccessService getInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        return createInstance();
    }

    private static synchronized DBAccessService createInstance() {
        if (INSTANCE != null) {
            return INSTANCE;
        }
        DBAccessService instance = new DBAccessService();
        INSTANCE = instance;

        return INSTANCE;
    }
}

有2个方面需要注意:

  1. getInstance()同步的,所以在初始化 INSTANCE 之后,同步是没有成本的
  2. createInstance()是同步的

所以,问题是:这段代码有什么问题吗?它是否合法且始终是线程安全的?

4

5 回答 5

8

您需要声明它才能INSTANCE工作volatile

private static volatile DBAccessService INSTANCE;

请注意,它仅适用于 Java 5 及更高版本。请参阅“双重检查锁定已损坏”声明

于 2011-05-12T09:49:04.207 回答
4

为了在实践中解决这个特定的问题Java 并发(由基本上编写 java.util.concurrent 库的团队编写)推荐延迟初始化持有者类成语(我的副本中的第 348 页,清单 16.6,而不是 16.7)

@ThreadSafe
public class DBAccessServiceFactory {
  private static class ResourceHolder {
    public static DBAccessService INSTANCE = new DBAccessService();
  }
  public static DBAccessService getResource() {
    return ResourceHolder.INSTANCE;
  }
}

这始终是合法且线程安全的。我不是专家,所以我不能说这比你的代码更好。然而,鉴于这是 Doug Lea 和 Joshua Bloch 推荐的模式,我总是在你或我发明的代码上使用它,因为它很容易出错(正如这个问题的错误答案的数量所证明的那样)。

与他们说的不稳定问题有关:

JMM(Java 5.0 及更高版本)中的后续更改使 DCL 在资源变为易失性的情况下能够工作......但是,惰性初始化持有者习语提供了相同的好处并且更容易理解。

于 2011-05-12T10:10:06.370 回答
2

在本文中,如果您使用单独的 Singleton 类,则声称“双重检查日志记录”不是问题:

public class DBAccessHelperSingleton {
    public static DBAccessHelper instance = new DBAccessHelper(); 
} 

它具有相同的优点:该字段在第一次被引用之前不会被实例化。

如前所述,如果您想保持现有状态,那么如果您volatile只针对 JDK >= 5,它就会丢失。

于 2011-05-12T09:55:53.307 回答
1

它不是线程安全的,请查看优秀的文章。不能保证一个线程会看到完全初始化的 DbAccessService 实例。为什么不使用简单

public class DBAccessService() {
     public static DBAccessService INSTANCE = new DBAccessService();
}
于 2011-05-12T09:58:56.740 回答
0

看起来不错。如果两个线程调用 getInstance() 并且 INSTANCE 未初始化,则只有一个线程能够继续执行 createInstance() 并且第二个线程已经看到该实例不为空。

唯一的事情是volatileINSTANCE 的关键字。否则java可以缓存它。

于 2011-05-12T09:50:02.667 回答