【问题标题】:Can the following Singleton be unsafe in multithreaded environment下面的Singleton在多线程环境中是否不安全
【发布时间】:2014-09-01 16:40:57
【问题描述】:

我想确保我的 Singleton 实例安全可用且同步最少,但我对同步块之外的第一个 if 子句表示怀疑。 INSTANCE 在未完全构造时是否有可能具有非空值?如果是这样,我该如何解决这个问题。

我认为包含整个get() 块会降低效率,因为有太多的配置变量必须通过get() 方法从程序的不同部分每秒读取数千次。

public class ConfsDBLoader {

    private static ConfsDBLoader INSTANCE = null;
    private static final Object lock = new Object();

    private ConfsDBLoader() { //Codes loading the db objects
    }

    public static ConfsDBLoader get(){
        if(INSTANCE != null){
            return INSTANCE;
        } else {
            synchronized(lock){
                if(INSTANCE == null){
                    INSTANCE = new ConfsDBLoader();
                }
                return INSTANCE;
            }
        }
    }

}

注意:我不能使用静态初始化,因为我的 hibernate sessionFactory 是静态初始化的,我希望拥有相互需要的复杂静态结构。事实上,我已经有了它,我不想让它变得越来越复杂,并调查这些静态属性在哪里尝试相互使用。

【问题讨论】:

  • 我假设您知道一些更简单的替代方案已经存在了大约十年。您能否告诉我们您的考虑以及为什么它们不合适?
  • 您正在使用一个非常古老且非常知名的反模式。它不是线程安全的。当第一个调用者仍在初始化它时,第二个调用者可以找到一个非空实例。
  • 你的意思是我应该嵌套两个同步?但效率不高!
  • 我建议你使用 SingletonHolder 或 enum,根本不使用同步。你能说说你为什么不使用这些吗?
  • @Johnny:他们都不急于求成。您正在尝试“优化”一个根本不存在的问题。

标签: java multithreading synchronization singleton


【解决方案1】:

没有。没有足够的同步来确保您在 INSTANCE 上看到正确的值。如果您的 ConfsDBLoader,您可能会看到一个非空但损坏的实例,因为在另一个线程调用 getInstance() 时它可能没有正确构造。

您有 3 个选择:

  • 渴望初始化并最终确定
  • 同步整个方法
  • 创建实例volatile

【讨论】:

  • 我拒绝前两个,但它只适用于 volatile 关键字吗?!这就是我的怀疑。它是否足够高效?
  • @Johnny 定义“足够高效”是什么意思?
  • @Johnny 同步整个方法有一些开销,但你打算多久调用一次 get 方法?在某个时候,你会在你的类中缓存一个本地实例,对吧?如果我必须在两者之间进行选择,并且急切地加载对象不是问题,我只会选择选项 1。
  • @Peter Lawrey ...设置实例时线程之间的同步是原子的吗?我从来没有真正尝试过找到打破 volatile 的方法,但这是我的第一个想法,因为我只是真正知道它是一种使值保持同步的方法,而不是从原子性的上下文中。很高兴知道!
  • @coffeeaddict volatile 有你需要的内存屏障。
猜你喜欢
  • 2015-07-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多