共享char数组

时间:2018-05-30 04:55:32

标签: java multithreading java-memory-model

假设我有以下内容:

char[] shared = new char[]{'a', 'b'};
ExecutorService exec = Executors.newCachedThreadPool();
Future<?> f1 = exec.submit(() -> shared[0] = 'A');
Future<?> f2 = exec.submit(() -> shared[1] = 'B');
f1.get(); f2.get();
System.out.println(shared);

我希望这通常会打印出来:

'AB'

但也可以打印:

'aB'

'bA'

我想知道如果shared不是易失性的,一个线程可能会在保存其对应的char元素的更改时保存其相邻的字节,这在本地处理器缓存中是陈旧的,并且踩踏另一个线程所做的更改。我觉得这个问题的答案是“不”,因为我认为它违反了Java的内存模型,它说每当一个线程加入时,它所做的所有动作都会在加入之前发生。但如果是这样的话,我很好奇这是怎么防止的?我的理解是,当刷新字节时,整个缓存行被刷新,我想象一个char []数组会有多个元素位于同一个缓存行中。

2 个答案:

答案 0 :(得分:1)

不,这是不可能发生的。如果查看生成的字节码,可以自己验证。

// lambda synthetic method
private static synthetic lambda$main$0([C)Ljava/lang/Character; throws java/lang/Exception
    L0
    LINENUMBER 11 L0
    ALOAD 0           // Loads reference of array "shared"
    ICONST_0          // Load index 0 onto the stack
    BIPUSH 65         // 65 = letter A in unicode
    DUP_X2            
    CASTORE           // Consumes reference, index, and value from stack
    INVOKESTATIC java/lang/Character.valueOf (C)Ljava/lang/Character;
    ARETURN
    MAXSTACK = 4
    MAXLOCALS = 1

CASTORE操作码只需要3个参数,数组引用,索引和存储到字符数组中的值。没有任何迹象表明数组赋值会尝试复制数组(或任何相邻的数组值),因为基本上没有这种转换有用的地方。

答案 1 :(得分:0)

已在Java Language Specification中完全指定:

  

17.6。单词撕裂

     

Java虚拟机实现的一个考虑因素是,每个字段和数组元素都被认为是不同的。对一个字段或元素的更新不得与任何其他字段或元素的读取或更新交互。特别是,两个分别更新字节数组的相邻元素的线程不得干涉或交互,也不需要同步以确保顺序一致性。

     

某些处理器不提供写入单个字节的能力。在这样的处理器上执行字节数组更新是非法的,方法是简单地读取整个字,更新适当的字节,然后将整个字写回内存。这个问题有时被称为单词撕裂,在不能轻易地单独更新单个字节的处理器上,将需要其他方法。

在最坏的情况下,这将意味着使用CAS指令或类似指令执行此类数组更新,以确保不发生中间更新。但据我所知,x86 / x64架构不受此影响,因为它具有修改较小尺寸项的指令,并且它将处理使另一个CPU内核的缓存行无效的操作,以确保其他CPU内核将获取已更改的内容。数据,然后再执行自己的更新。当然,这仍然比两个CPU内核在不同的缓存行上执行更新要慢。