并发编程中通常使用 volatile 保证线程间可见性,但是被 volatile 修饰的数组中元素是无法保证线程间可见的,例如 ConcurrentHashMap 对这一问题采用 Unsafe 的方式访问 U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
关于 volatile 示例
暂无关于 volatile array 失败测试用例
解释
在 Java 中无论是基础类型数组还是对象类型数组底层都是一个对象,区别是数组对象在对象头中多出 32 位数组长度标识。
普通对象需要保证线程间可见通常修饰 volatile 关键字,若对象内成员未修饰 volatile 关键字,成员是无法保证线程间可见的。在初始化数组或者创建数组是无法为元素修饰 volatile 关键字的,同样的解释还有:
这个是因为Java数组在元素层面的元数据设计上的缺失,无法表达元素是final、volatile等语义,所以开了后门,使用getObjectVolatile用来补上无法表达元素是volatile的坑,@Stable用来补上final的坑,数组元素就跟没有标volatile的成员字段一样,无法保证线程之间可见性。
链接:https://www.jianshu.com/p/5808db3e2ace
保证数组元素可见性的几种方式
Unsafe.getVolatileObject
public class VolatileArray {
private volatile Object[] nums = new Object[]{new Object()};
private static Unsafe U;
private static long NUMS;
static {
try {
Class> clazz = Class.forName("sun.misc.Unsafe");
Field theUnsafe = clazz.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
U = (Unsafe) theUnsafe.get(null);
NUMS = U.objectFieldOffset(VolatileArray.class.getDeclaredField("nums"));
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
VolatileArray example = new VolatileArray();
new Thread(() -> {
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
example.nums[0] = new Object();
}).start();
new Thread(() -> {
while (true) {
Object[] objects = (Object[]) U.getObjectVolatile(example, NUMS);
if (objects[0] != null) {
System.out.println("index updated");
break;
}
}
}).start();
}
}
AtomicReferenceArray
public class VolatileArray {
private AtomicReferenceArray atomicReferenceArray = new AtomicReferenceArray<>(1);
public static void main(String[] args) throws InterruptedException {
VolatileArray example = new VolatileArray();
new Thread(() -> {
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
example.atomicReferenceArray.set(0, new Object());
}).start();
new Thread(() -> {
while (true) {
if (example.atomicReferenceArray.get(0) != null) {
System.out.println("index updated");
break;
}
}
}).start();
}
}
AtomicReferenceArray 实现与上方 Unsafe 代码思想基本一致。