即使其他线程在同步块中更新,线程也没有获得最新值?



我写这个程序来模拟field visibility

package com.example.threads.fieldvisibility.main;
public class ThreadVisibilityWithSyncKeyWord implements Runnable {
private boolean stop = false;
@Override
public void run() {
while (!stop) {
// System.out.println("stop is " + stop);
}
}
public static void main(String[] args) throws InterruptedException {
ThreadVisibilityWithSyncKeyWord test = new ThreadVisibilityWithSyncKeyWord();
Thread t = new Thread(test);
System.out.println("Starting Thread");
t.start();
Thread.sleep(5000);
System.out.println("Stopping Thread");
synchronized (test) {
test.stop = true;
}
t.join(5000);
System.out.println("Thread State: " + t.getState());
}
}

程序非常简单。我们有两个线程。主线程确实使用"test"对象在synchronized块中将标志"stop"更改为true。

我希望一旦主线程将其设置为 true,它就会使 while 循环终止。 但是即使主线程将标志设置为 true,另一个线程也看不到最新值(即使它在同步块中更新(。

奇怪的是,当我取消注释System.out.println()(在 while 内部(时,线程确实"看到"了最新值并终止。

我不理解这种行为。为什么另一个线程无法看到在主线程的同步块中更新的最新值。而在取消注释sysout后,是什么导致另一个线程看到标志的最新值?

当线程进入synchronized块时,通过从共享缓存/内存读取来保证看到访问变量的当前状态。在synchronized块内写入变量将保证变量写入共享缓存/内存。

发生了什么:

  • 创建test
  • main线程缓存test.stop
  • test缓存falsestop
  • test开始
  • test从本地缓存中读取stop的值 (!
  • main线程集test.stoptrue
  • 由于这是同步完成的,因此写入也会完成对共享缓存的写入
  • test不断从自己的缓存中读取stop,该缓存仍然false

将打印行添加到代码时,必须知道System.out.println涉及内部同步:

  • main线程设置test.stoptrue
  • 由于这是同步完成的,因此写入也会完成对共享缓存的写入
  • test执行println,在那一刻,内部同步导致test从共享缓存中读取新值

我认为主要问题是项目的结构,如果您将主线程类与可运行线程类分开,一切都会更容易。

这是具有以下结果的工作示例:(请注意,我在线程中添加了 1 秒睡眠以避免许多打印输出(

  • 起始线程
  • 停止为假
  • 停止为假
  • 停止为假
  • 停止为假
  • 停止线程
  • 停止为真
  • 线程状态:已终止

    包共项目;

    公共类 主 {

    public static boolean stop = false; 
    public static void main(String[] args) throws InterruptedException {
    ThreadVisibilityWithSyncKeyWord test = new ThreadVisibilityWithSyncKeyWord();
    Thread t = new Thread(test);  
    System.out.println("Starting Thread");
    t.start();
    Thread.sleep(5000);
    System.out.println("Stopping Thread");
    stop = true;
    t.join(5000);
    System.out.println("Thread State: " + t.getState());
    }
    

    }

    包共项目;

    public class ThreadVisibilityWithSyncKeyWord implements Runnable {

    @Override
    public void run() {
    while (!Main.stop) {
    try {
    Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
    System.out.println("stop is " + Main.stop);
    }
    }
    

    }