Java:如何使用数组元素进行双重检查锁定?

Java:如何使用数组元素进行双重检查锁定?,java,multithreading,arrays,synchronized,double-checked-locking,Java,Multithreading,Arrays,Synchronized,Double Checked Locking,这就是我的代码当前的样子: private boolean[] isInitialized = new boolean[COUNT]; private void ensureInitialized(int i) { if (! isInitialized[i]) { initialize(i); isInitialized[i] = true; } } 现在我想让它线程安全。我知道Java中的双重检查锁定是非常卑鄙的!!1,但由于经常调用ens

这就是我的代码当前的样子:

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

private void ensureInitialized(int i) {
    if (! isInitialized[i]) {
        initialize(i);
        isInitialized[i] = true;
    }
}
现在我想让它线程安全。我知道Java中的双重检查锁定是非常卑鄙的!!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实现被称为断开模式,因为它被证明是失败的,例如,请参阅。要解决这个问题,只需使用原子操作。下面是如何构建线程安全单例的示例:

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实现被称为断开模式,因为它被证明是失败的,例如,请参阅。要解决这个问题,只需使用原子操作。下面是如何构建线程安全单例的示例:

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);
        }
    }
}

您也可以考虑使用.P/IOMIZUPPLILE委托从.< /P> < P>您也可以考虑使用.PoiiSeri.MeoIZEPSUpPLER从./P>有证据表明java的同步是否不必要地慢?换句话说,是的,当有争用时它是慢的,但是当没有争用时它是快的。+1,但是一个解释是应该的。双重检查锁没有被打破,因为Java1.5使用volatile关键字使其工作正常。然而,正如OP正确地假设的那样,对数组元素的读写不是易变的。AtomicXArray类所做的是对每个元素执行volatile读取,查看源代码-unsafe.getObjectVolatile.-1,双重检查锁定是否被破坏。其余答案在技术上是正确的。“也许少一点说教,多一些技术解决方案?”蒂姆·本德(Tim Bender)——只需访问链接和/或阅读@Bozho的评论。或者至少试着用谷歌搜索一下“java双重检查”这个短语pattern@GregS-这很有趣,如果没有争用,为什么我们要使用同步呢?当您确定单线程延迟加载模式只使用单线程时,是否有证据表明java的同步速度太慢了?换句话说,是的,当有争用时它是慢的,但是当没有争用时它是快的。+1,但是一个解释是应该的。双重检查锁没有被打破,因为Java1.5使用volatile关键字使其工作正常。然而,正如OP正确地假设的那样,对数组元素的读写不是易变的。AtomicXArray类所做的是对每个元素执行volatile读取,查看源代码-unsafe.getObjectVolatile.-1,双重检查锁定是否被破坏。其余答案在技术上是正确的。“也许少一点说教,多一些技术解决方案?”蒂姆·本德(Tim Bender)——只需访问链接和/或阅读@Bozho的评论。或者至少试着用谷歌搜索一下“java双重检查”这个短语pattern@GregS-这很有趣,如果没有争用,为什么我们要使用同步呢?当你确信单线程惰性加载模式只使用单线程就足够了,我喜欢这样的回答:双重检查锁定真是太糟糕了!我不想在我的代码中看到任何内容:我支持它。在我找到这个成语后,我再也没有回头。这个习惯用法的优点是,图书馆的工作人员总是试图找到最好的方法,如果他们找到了更好的方法,他们会改变它,而你不必在代码库中重写上千个双重检查块。我喜欢这样的回答:双重检查锁定真是太可怕了!我不想在我的代码中看到任何内容:我支持它。在我找到这个成语后,我再也没有回头。这个习惯用法的优点是,库人员总是会尝试使用最好的方法,如果他们找到了更好的方法,他们会改变它,而您不必在代码库中重写上千个重复检查的块。