【问题标题】:Java: How to do double-checked-locking with an array element?Java:如何对数组元素进行双重检查锁定?
【发布时间】:2010-10-09 17:51:59
【问题描述】:

这是我的代码目前的样子:

private boolean[] isInitialized = new boolean[COUNT];

private void ensureInitialized(int i) {
    if (! isInitialized[i]) {
        initialize(i);
        isInitialized[i] = true;
    }
}

现在我想让它线程安全。我知道 Java 中的双重检查锁定是“teh 3vilness!!1”,但由于ensureInitialized 可能经常被调用,我不希望它被同步。所以我正在考虑这样做:

private boolean[] isInitialized = new boolean[COUNT];

private void ensureInitialized(int i) {
    if (! isInitialized[i]) {
        synchronized (this) {
            if (! isInitialized[i]) {
                initialize(i);
                isInitialized[i] = true;
            }
        }
    }
}

现在我必须做些什么才能使这个线程真正安全?
一些子问题:

  • 没有必要让isInitialized volatile,因为变量没有改变,对吧?
  • 数组元素发生了变化,如何让它们变得易变?
  • 一般有更好的方法来做到这一点吗?

(还要注意这是一个实例方法,所以静态初始化器不起作用)

【问题讨论】:

    标签: java multithreading arrays synchronized double-checked-locking


    【解决方案1】:

    您也可以考虑使用来自guava librarySuppliers.memoize(Supplier<T> delegate)

    【讨论】:

    • 我喜欢这样的回答:双重检查锁定是如此的暴行!我不想在我的代码中看到任何一点:)
    • 我同意这一点。找到这个成语后,我再也没有回头。这个习语的优点是图书馆的人总是会试图倾向于最好的方法,如果他们找到更好的方法,他们会改变它,你不必在你的代码库中重写你的数千个双重检查块。
    【解决方案2】:

    请注意,双重检查的 java 实现被称为“破坏模式”,因为它被证明是失败的(例如参见 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)。要解决这个问题,只需使用原子操作。以下是如何构建线程安全单例的示例:

    static AtomicReferenceArray<Boolean> instance = 
       new AtomicReferenceArray<Boolean>(COUNT);
    
    private void ensure(int i)
    {
        if(!instance.get(i) )
        {
            synchronized(this){
            if( !instance.get(i) ){
               initialize(i);
               instance.set(i, true);
            }
        }
    }
    

    【讨论】:

    • 是否有任何证据表明 java 的同步速度过慢?换句话说,是的,有竞争时它很慢,但没有竞争时它很快。
    • +1,但需要解释一下。自 Java 1.5 以来,双重检查锁没有被破坏 - 使用 volatile 关键字使其工作正常。但是,正如 OP 正确假设的那样,对数组元素的读取和写入不是易失性的。 AtomicXArray 类所做的是对每个元素进行 volatile 读取(参见源代码 - unsafe.getObjectVolatile())。
    • -1,双重检查锁定没有被破坏。其余的答案在技术上是正确的。也许更少的说教和更多的技术解决方案?
    • @Tim Bender - 只需转到链接和/或阅读@Bozho 的评论。或者至少尝试用谷歌搜索“java双重检查”“破坏模式”
    • @GregS - 这很有趣,如果没有“争用”,为什么我们应该使用同步?当您确定仅由单线程延迟加载模式使用的单例就足够了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-27
    • 1970-01-01
    • 1970-01-01
    • 2019-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多