数组的易变阵列

maa*_*nus 9 java arrays multithreading volatile

我上课了

private volatile long[][] data = new long[SIZE][];
Run Code Online (Sandbox Code Playgroud)

最初只包含空值和访问它的方法.当它命中null元素时,它会创建一个long[]并存储以备将来使用.此操作是幂等的,并且在同一元素上浪费时间的多个线程不是问题.

没有线程必须看到未完全填充的元素.我希望以下代码做得对:

long[] getOrMakeNewElement(int index) {
    long[] element = data[index]; // volatile read
    if (element==null) {
        element = makeNewElement(index); // local operation
        data = data; // ugliness 1
        data[index] = element;
        data = data; // ugliness 2
    }
    return element;
}
Run Code Online (Sandbox Code Playgroud)

第一个丑陋是确保其他线程理论上可以看到所做的更改element.它们实际上无法访问它,因为它还没有存储.但是,在下一个像element存储和另一个线程可能会或可能不会看到这个商店,所以AFAIK第一个丑陋是必要的.

然后第二个丑陋只是确保其他线程看到新的data包含element.这里奇怪的是两次使用丑陋.

对于安全发布是否必要充分

注意:虽然这个问题类似于这一个,这是没有重复的,因为它与修改现有的一维数组,而不是建立一个交易.这使答案清楚.

更新

注:这是没有生产代码,我知道和不关心的替代品(AtomicReferenceArray,synchronized,无论...).我写了这个问题是为了更多地了解JMM.这是一个真实的代码,但仅仅用于我对项目Euler的愚弄,并且没有动物在这个过程中受到伤害.

我想,一个安全而实用的解决方案

class Element {
    Element(int index) {
        value = makeNewElementValue(index);
    }
    final long[] value;
}

private volatile Element[] data = new Element[SIZE];
Run Code Online (Sandbox Code Playgroud)

其中Element通过最终字段语义确保可见性.

正如user2357112所指出的,当多个线程写入相同时data[index],还存在(IMHO无害)数据竞争,这实际上很容易避免.虽然读取速度必须很快,但制作新元素的速度足够慢,无法进行任何同步.它还可以更有效地初始化数据.

use*_*ica 0

您的代码存在数据竞争。如果线程A和B各自执行

        data = data; // ugliness 1
Run Code Online (Sandbox Code Playgroud)

然后每次执行

        data[index] = element;
Run Code Online (Sandbox Code Playgroud)

然后每次执行

        data = data; // ugliness 2
Run Code Online (Sandbox Code Playgroud)

两者之间的写入之间没有发生之前的data[index]关系。我不完全确定 JMM 在存在数据争用的情况下能提供什么保证,但保证不多。使用这段代码并不是一个好主意。