java - Java volatile字段跨线程读写协调理解

标签 java multithreading java.util.concurrent java-memory-model

我有以下代码:

 private volatile boolean run = true;

 private Object lock =new Object();

…………

Thread newThread = new Thread(new Runnable() {

    @Override
        public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {

            e.printStackTrace();
        }
            synchronized (lock) {
                System.out.println(Thread.currentThread().getName()
                        + " run:" + run);

                System.out.println(Thread.currentThread().getName()
                        + " setting run to false");

                run = false;

                System.out.println(Thread.currentThread().getName()
                        + " run:" + run);
            }
        }});

newThread.start();

while(true) {//no synchronization, so no coordination guarantee
    System.out.println(Thread.currentThread().getName() + "* run: "+run);

    if(run == false) {
    System.out.println(Thread.currentThread().getName() + "** run: "+run+"\nExiting...");
    System.exit(0);
    }
}




which generates the following output:



main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
Thread-0 setting run to false
Thread-0 run:false
main* run: true    <- what causes this???
main** run: false
Exiting...

我试图理解为什么在主线程中发生 ma​​in* run: true 的异常,考虑到 run 是一个易变的字段并且根据 Java 内存模型规范中,Thread-0 中的 volatile 写入应该立即被 main 线程可见。我知道 Thread-0 中的同步在这里是无关紧要的,但我对 volatile 的这种行为感到困惑。我在这里错过了什么?

另一个更奇怪的运行产生了这个:

main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main** run: false
Exiting...
Thread-0 run:false

或者这种行为是可以预料的,如果是,为什么?谢谢。

编辑:正如评论中所问,我正在用我有时但并非总是看到的预期输出更新帖子:

main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
main* run: true
Thread-0 setting run to false
main* run: true
main* run: true
main* run: true
Thread-0 run:false
main** run: false
Exiting...

换句话说,我不想看到:

main* run: true 

出现在

之后
Thread-0 run:false

main** run: false
Exiting...

出现在

之前
Thread-0 run:false

最佳答案

我没有看到问题。这里的锁是没用的。 volatile 也意味着变量在自身内部是同步的。这里发生了什么。每当有多个线程时,每个线程都会自行运行而不关心其他线程。所以在这种情况下,我们有两个线程:main 和 thread-0。 Main 自行运行并到达打印变量 run 的位置,因此它会打印它。另一个线程 hibernate 了一点(这应该无关紧要,也不应该是让其他线程先工作的方式),然后将变量 run 更改为 false。主线程读取新值并存在

按照时间顺序你就明白了

Thread newThread = new Thread(new Runnable() {

@Override
    public void run() {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {

        e.printStackTrace();
    }
        synchronized (lock) {
            System.out.println(Thread.currentThread().getName()
                    + " run:" + run);

            System.out.println(Thread.currentThread().getName()
                    + " setting run to false");

            run = false; //<---- time_4

            System.out.println(Thread.currentThread().getName()
                    + " run:" + run); //<---- time_5
        }
    }});

newThread.start();

while(true) { //<---- time_2
    System.out.println(Thread.currentThread().getName() + "* run: "+run); //<--- time_3 getting the value of run variable. //<---- time_6 printing

    if(run == false) { //<---- time_1 (run == true) // <---- 2nd iteration time_7 (run == false)
    System.out.println(Thread.currentThread().getName() + "** run: "+run+"\nExiting..."); //<---- time_8
    System.exit(0);
    }
}

无论如何,这里是如何修复你的代码以获得预期的输出(注意:volatile 在这里没有做任何事情):

synchronized (lock) {
                if(run == false) {
                    System.out.println(Thread.currentThread().getName() + "** run: "+run+"\nExiting...");
                    System.exit(0);
                }
            }

这就是 volatile 对变量 run 的基本作用:

// run = false; //becomes ========
synchronized(someLock) {
    run = flase;
}
// =======================


//System.out.println(run); //becomes =========   
synchronized(someLock) {
    boolean tmpBoolean = run;
}
System.out.println(tmpBoolean);
//=================

关于java - Java volatile字段跨线程读写协调理解,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39312933/

相关文章:

java - 未能获取信号量的线程会发生什么情况?

java - Phaser - 如何将其用作 CountDownLatch(1)?

java - 如何创建 Elasticsearch 节点并指定用于索引和搜索的默认搜索分析器

java - 为什么我无法从此枚举中获取所需的数据?

java - 安卓应用程序 : page stopped working

java - JAVA中的多线程概念

java - 完成 future : How to apply a function to multiple CompletableFutures?

java - 如何使用所有的Unix环境变量?

java - 使用 == 而不是 equals 来比较 Java 中的不可变对象(immutable对象)是否可以

c# - 多线程和构造函数完整性