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

标签 java multithreading volatile

根据这个问题的答案( 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/

相关文章:

java - 隐藏的 JDialog 在 Windows 10 任务栏预览中仍然可见

java - 如何从日期选择器中获取字符串形式的值,即使它不是日期格式(JavaFX)?

c - 在 C 中正确丢弃 volatile 变量内容的替代方法是什么?

java - Guava 事件总线 : NullPointerException when Posting from Thread to UI

c - 为什么在一个序列点内最多只能有一个具有 volatile 限定类型的读取访问?

java - 在数据竞争期间,线程能否读取 volatile 变量的初始空值?特别是在构造函数中为其分配了非空值时?

java - 从 google web 应用程序中的 servlet 发送电子邮件

java - 与 Java nanotime() 相比,为什么 C++ 中的 clock_gettime 给我不同的值

c# - 使用 Dispose() 或终结器来清理托管线程?

c++ - 如何在 C++ 中创建 glfw 线程?