gpt4 book ai didi

java volatile 数组,我的测试结果与预期不符

转载 作者:行者123 更新时间:2023-12-02 10:28:26 26 4
gpt4 key购买 nike

根据这个问题的答案( Java volatile array? ),我做了以下测试:

public class Test {
public static volatile long[] arr = new long[20];
public static void main(String[] args) throws Exception {
new Thread(new Thread(){
@Override
public void run() {
//Thread A
try {
TimeUnit.MILLISECONDS.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
arr[19] = 2;
}
}).start();
new Thread(new Thread(){
@Override
public void run() {
//Thread B
while (arr[19] != 2) {
}
System.out.println("Jump out of the loop!");
}
}).start();
}
}

据我所知,对于数组对象,volatile关键字仅保证arr引用的可见性,不保证数组中元素的可见性。然而当线程A改变了arr[19]时,线程B发现了arr[19]的变化并跳出了循环。

那么问题出在哪里呢?

最佳答案

让我首先对您的示例进行修改:

public class Test {
public static long[] arr = new long[20]; // Make this non-volatile now
public static volatile int vol = 0; // Make another volatile variable

public static void main(String[] args) throws Exception {
new Thread(new Thread(){
@Override
public void run() {
//Thread A
try {
TimeUnit.MILLISECONDS.sleep(1000);
arr[19] = 2;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(new Thread(){
@Override
public void run() {
//Thread B
while (true) {
int i = vol;

if (arr[19] == 2) {
break;
}
}
System.out.println("Jump out of the loop!");
}
}).start();
}
}

你会意识到这也会导致线程 B 跳出循环(除非这个症状是 JIT 特有的,而我的恰好就是这样做的)。神奇之处就在于这个int i = vol;——或者更准确地说,读取一个 volatile 变量。删除该行将导致线程 B 无限地停留在循环内。

因此,似乎对 volatile 变量的任何读取(即任何 volatile 读取)似乎都会检索最新的值(包括其他非 volatile 值)。

我尝试研究 JLS,但它太复杂,我无法完全理解。我看到一篇文章here描述了可见性保证。

摘自文章:

If Thread A reads a volatile variable, then all all variables visible to Thread A when reading the volatile variable will also be re-read from main memory.

(忽略文章中“all all”的拼写错误。)

在这种情况下,当线程读取 volatile 变量时,似乎主内存中的所有数据都会更新回CPU缓存。

<小时/>

其他有趣的发现:如果您向线程 B 添加另一个 Thread.sleep()(例如 sleep 50 毫秒),即使没有读取 volatile <,循环也会设法退出 变量。令人惊讶JLS 17.3声明如下:

It is important to note that neither Thread.sleep nor Thread.yield have any synchronization semantics. In particular, the compiler does not have to flush writes cached in registers out to shared memory before a call to Thread.sleep or Thread.yield, nor does the compiler have to reload values cached in registers after a call to Thread.sleep or Thread.yield.

我再次不确定此症状是 JIT 还是 JRE 特定的。

关于java volatile 数组,我的测试结果与预期不符,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53753792/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com