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

发布于 2024-11-06 16:57:03 字数 812 浏览 6 评论 0原文

这段代码是否解决了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;
    }
}

有两个方面需要注意:

  1. getInstance()同步的,因此 INSTANCE 初始化后,同步
  2. createInstance() 是没有任何成本的是同步的

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

Does this code solve the double checked locking issue in 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;
    }
}

There are 2 aspects to pay attention:

  1. getInstance() is not synchronized, so after INSTANCE is initialized there is no cost for synchronization
  2. createInstance() is synchronized

So, the question is: does this code have any issues? Is it legal and always thread-safe?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

岁月蹉跎了容颜 2024-11-13 16:57:03

您需要将 INSTANCE 声明为 volatile 才能正常工作:

private static volatile DBAccessService INSTANCE;

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

You need to declare INSTANCE as volatile for it to work:

private static volatile DBAccessService INSTANCE;

Note it only works with Java 5 and later. See The "Double-Checked Locking is Broken" Declaration.

秉烛思 2024-11-13 16:57:03

为了解决这个特定问题实践中的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 能够在资源变得易失性的情况下工作...但是,惰性初始化持有者习惯用法提供了相同的好处并且更易于理解。

For solving this particular question Java concurrency in practice (written by the team who basically wrote the java.util.concurrent library) recommends the Lazy Initialization holder class idiom (page 348 in my copy, Listing 16.6, not 16.7)

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

This is always legal and Thread-safe. I'm not an expert, so I can't say this is better than your code. However, given that it is the pattern recommended by Doug Lea and Joshua Bloch, I'd always use it over code you or I have invented, as it is so easy to make mistakes (as demonstrated by the number of wrong answers to this question).

Related to the volatile issue they say:

Subsequent changes in the JMM (Java 5.0 and later) have enabled DCL to work if resource is made volatile ... However the lazy initialization holder idiom offers the same benefits and is easier to understand.

也只是曾经 2024-11-13 16:57:03

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

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

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

如前所述,如果您想保持现有状态,则在您仅针对 JDK >= 5 的情况下缺少 易失性

In this article it is claimed that "double checked logging" is not an issue if you use a seperate Singleton class:

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

It has the same advantage: The field is not instantiated before being references the first time.

As stated previously, if you want to keep like you have it volatile is missing in case you are only targetting JDK >= 5.

<逆流佳人身旁 2024-11-13 16:57:03

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

public class DBAccessService() {
     public static DBAccessService INSTANCE = new DBAccessService();
}

It is not thread safe, please check an excelent article. There is no guarantee, that one thread will see fully initialized instance of DbAccessService. Why not using simple

public class DBAccessService() {
     public static DBAccessService INSTANCE = new DBAccessService();
}
少女净妖师 2024-11-13 16:57:03

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

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

Looks fine.
If two threads call getInstance() and INSTANCE is not initalized, only one thread will be able to proceed with createInstance() and the second one will already see that instance is not null.

The only thing is volatile keyword for INSTANCE. Otherwise java can cache it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文