繁体   English   中英

对阵列和数组元素的AtomicReference改变了可见性

[英]AtomicReference to array and array element changes visibility

Java是否保证在将数组引用存储在AtomicReference之前对线程A完成的数组元素的更新将始终对获取此引用的线程B可见?

换句话说,执行此操作的可能输出是什么:

class References {
    AtomicReference<String[]> refs = new AtomicReference<>(new String[]{"first"});

    public void add(String s) {
        refs.updateAndGet(oldRefs -> {
            String[] newRefs = new String[oldRefs.length + 1];
            System.arraycopy(oldRefs, 0, newRefs, 0, oldRefs.length);
            newRefs[oldRefs.length] = s;
            return newRefs;
        });
    }

    public static void main(String[] args) {
        References r = new References();
        new Thread(() -> r.add("second")).start();
        System.out.println(Arrays.toString(r.refs.get()));
    }
}

以上只能打印[first][first, second] ,还是可以获得[first, null][null, null]

java.util.concurrent.atomic的javadoc状态:

compareAndSet和所有其他读取和更新操作(如getAndIncrement都具有读取和写入volatile变量的内存效果。

这似乎不能保证数组的非易失性元素,只有数组引用本身。

给变量的性写将保证发生前,将一直同一个变量的后续性读之前发生的一切。

JLS的相关规则是:

17.4.4。 同步顺序

  • 对易失性变量v(第8.3.1.4节)的写入与任何线程对v的所有后续读取同步(其中“后续”根据同步顺序定义)。

17.4.5。 发生在订单之前

可以通过先发生关系来排序两个动作。 如果一个动作发生在另一个动作之前,那么第一个动作在第二个动作之前可见并且在第

如果我们有两个动作x和y,我们写hb(x,y)来表示x发生在y之前。

  • 如果x和y是同一个线程的动作,并且x在程序顺序中出现在y之前,那么hb(x,y)。
  • 如果动作x与后续动作y同步,那么我们也有hb(x,y)。

  • 如果是hb(x,y)和hb(y,z),那么hb(x,z)。

由于AtomicReference保证您的数组引用仅以易失方式存储/加载(并且一旦编写,您不修改现有数组),这足以保证System.arrayCopy() (和行的结果)的可见性跟随它)为任何人调用refs.get()

然而,构造仍然不是完全不透水的,因为通过refs.get()获得数组引用的任何人都可以继续并在没有AtomicReference保护的情况下对元素进行更改。

CopyOnWriteArrayList与此非常相似(它使用ReentrantLockvolatile数组字段而不是AtomicReference ),除了它还保证没有人可以获得底层数组并以不安全的方式使用它。

您不能在数组中获取任何null元素,因为您没有修改代码中的数组元素,而是每次都创建一个新元素( String[] newRefs = new String[oldRefs.length + 1]; )。 由于存储数组引用并且未修改其元素,因此无法看到null元素。 如果在您的代码中您有类似的东西:

   refs.updateAndGet(oldRefs -> {
        if (oldRefs.size>0) oldRefs[0]=null;//is an example just for "fun"
        String[] newRefs = new String[oldRefs.length + 1];
        System.arraycopy(oldRefs, 0, newRefs, 0, oldRefs.length);
        newRefs[oldRefs.length] = s;
        return newRefs;
    });

然后你可以看到不同的东西,有些用户可能会在第一个元素中看到空值

更新 :由于AtomicReference仅用于对Array的引用,因此您可以使用AtomicReferenceArray以更安全的方式访问数组元素

对数组的AtomicReference与其他任何内容都没有区别 - 它只是原子的引用 ,因此具有相关的内存障碍。 访问数组就像任何其他对象一样 - 没有额外的保护。

因此,您将始终获得[first][first, second]并且没有其他选项,因为您正在创建新阵列,填充它并将其放回原始保护的旧引用中。

Java数组不是很擅长调整大小。 如果你想要一个可调整大小的结构,你最好使用一个ArrayList 如果您想要并发访问它,请使用CopyOnWriteArrayList ,这实际上是您尝试在代码中实现的内容。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM