声明数组为 volatile 并不会使其字段具有 volatile 访问权限。你所声明的是引用本身是 volatile,而不是它的元素。
换句话说,你声明了一个“具有 volatile 元素集合”而不是“一组具有 volatile 元素”的东西。
解决方法是如果要使用整数,请使用 AtomicIntegerArray
。另一种方法(但有点丑陋)是每次编辑字段时重新编写数组的引用。
你可以通过以下方式实现:
arr = arr;
(就像我说的...很丑)
volatile
关键字确保 happens-before 关系。 - mikeAtomicLongArray、AtomicIntegerArray和AtomicReferenceArray(java.util.concurrent.atomic)。
编辑: 在Java中,数组是对象。如果将对该对象的引用设置为volatile,则交换对数组的引用时,其他线程可以看到它。 但是,这并不适用于数组值本身。
更好地理解Java内存模型,实际上有可能避开使用Atomic*Array。使用volatile读取和正常写入的先发生-后发生关系可以实现:
如果线程A写入一些非volatile的内容以及一个volatile变量之后,只有当线程B首先读取volatile变量时,才能保证线程B也看到非volatile内容的更改。 另请参见: Happens-before relationships with volatile fields and synchronized blocks in Java - and their impact on non-volatile variables?
对于数组,这意味着: 在写入数组后,写入某个volatile状态变量(确保写操作实际上更改了volatile状态变量!) 在从数组读取时,首先读取volatile状态变量,然后再访问数组。 只要之前已经发生过,volatile读操作应该使所有其他写操作都可见。
旧版:
写入自身引用arr=arr
实际上没有帮助。
您写入的是数组的地址arr
,而不是字段arr[i]
的值。因此,您仍然不能获得arr[i]
的volatile属性(这是您想要的),但只能获得存储地址arr
的volatile属性。
Jeremy Manson的上述博客文章详细解释了这一点: http://jeremymanson.blogspot.com/2009/06/volatile-arrays-in-java.html
他最好的解决方案是使用Atomic*Array,即对于通用类型使用AtomicReferenceArray(也存在基本类型的特殊形式)。我想象不出这特别高效,尤其是因为它可以提供比您需要的更多的属性(原子性>>volatile)。
另一个选择是使用指针结构,其中容器使用易失性指针字段。但这种方法效率不高...
static class Cell<T> {
volatile T elem;
}
private Cell<T>[] alloc(int size){
Cell<T>[] cells = (Cell<T>[]) (new Cell[size]);
return cells;
}
volatile Cell<T>[] arr;
Cell<T>[] newarr = alloc(16);
for (int i = 0; i < newarr.length; i++) {
newarr[i] = new Cell<>();
}
arr = newarr;
这些单元格使内容也变得不稳定。在预先分配单元格后,我将新数组赋给了易挥发的数组…这就牺牲了额外的单元格内存,但是这是可以管理的。
String[] sa = ...
VarHandle avh = MethodHandles.arrayElementVarHandle(String[].class);
boolean r = avh.compareAndSet(sa, 10, "expected", "new");