Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/arrays/13.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 对数组和数组元素的原子引用更改可见性_Java_Arrays_Multithreading_Atomic - Fatal编程技术网

Java 对数组和数组元素的原子引用更改可见性

Java 对数组和数组元素的原子引用更改可见性,java,arrays,multithreading,atomic,Java,Arrays,Multithreading,Atomic,Java是否保证在将数组引用存储到原子引用中之前线程A对数组元素所做的更新对获得该引用的线程B始终可见 换句话说,执行此操作的可能输出是什么: class References { AtomicReference<String[]> refs = new AtomicReference<>(new String[]{"first"}); public void add(String s) { refs.updateAndGet(oldRe

Java是否保证在将数组引用存储到
原子引用中之前线程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()));
    }
}
类引用{
AtomicReference refs=新的AtomicReference(新字符串[]{“first”});
公共无效添加(字符串s){
参考文献更新日期(旧参考文献->{
字符串[]newRefs=新字符串[oldRefs.length+1];
System.arraycopy(oldRefs,0,newRefs,0,oldRefs.length);
newRefs[oldRefs.length]=s;
返回新引用;
});
}
公共静态void main(字符串[]args){
参考文献r=新参考文献();
新线程(()->r.add(“第二个”)).start();
System.out.println(Arrays.toString(r.refs.get());
}
}
上面只能打印
[first]
[first,second]
,或者也可以得到类似
[first,null]
[null,null]
的结果吗

java.util.concurrent.atomic的javadoc声明:

compareAndSet
和所有其他读取和更新操作,如
getAndIncrement
都具有读取和写入易失性变量的内存效应


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

您不能在数组中获得任何空元素,因为您没有在代码中修改数组元素,而是每次都创建一个新的(
String[]newRefs=newstring[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;
    });
然后您可以看到一些不同的内容,一些用户可能会在第一个元素中看到null值

更新
由于AtomicReference仅适用于对数组的引用,您可以使用它以更安全的方式访问数组元素

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

因此,在创建新数组、填充它并将其放回受原子保护的旧引用时,您将始终获得
[first]
[first,second]
,而没有其他选项


Java数组不太擅长调整大小。如果您想要一个可调整大小的结构,最好使用
ArrayList
。如果您想同时访问它,请使用一个基本上就是您试图在代码中实现的变量。

对变量的易失性写入将保证在它之前发生的所有事情都会发生在对同一变量的后续易失性读取之前

政府的有关规定如下:

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
保证数组引用只以易失性的方式存储/加载(并且一旦写入,就不会修改现有数组),这足以保证调用
refs.get()
的任何人都能看到
System.arrayCopy()
(及其后的行)

但是该构造仍然不是完全防水的,因为通过
refs.get()
获取数组引用的任何人都可以在不受
原子引用保护的情况下继续对元素进行更改


CopyOnWriteArrayList
的工作原理与此非常类似(它使用了
ReentrantLock
volatile
数组字段的组合,而不是
AtomicReference
),除了保证没有人可以获得底层数组并以非安全的方式使用它之外。

您将始终获得
{“first”,“second”}
System.arrayCopy()
在完成其任务之前不会返回(否则将非常不可靠!)如果您将数组分配给
最终的
字段,您将有这样的保证,原子引用
我想您不会返回。顺便说一句,我可能会使用
CopyOnWriteArrayList
,这为您提供了这样一个集合所能提供的所有保证。仔细想想,它保证您只会看到
[first]
[first,second]
,而不会看到其他内容。@fge您所说的
arraycopy
实际上只保证第一个元素是“first”,我真正关心的是
newRefs[oldRefs.length]=s
赋值是否发生在使数组引用可见之前。我所问的空元素不是来自删除现有数组中的引用,而是来自在所有元素都设置为适当的值之前可能释放数组引用