java 同步处理期间未应用变量更改

java variable change not applied during synchronous processing

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test extends Thread {
    private boolean running;
    private Lock cleanupLock;
    
    @Override
    public void run() {
        cleanupLock.lock();
        while (running) {
            System.out.println("running!");
        }
        cleanupLock.unlock();
    }

    private void cleanup() {
        running = false;
        cleanupLock.lock(); //wait for thread
        System.out.println("cleanup!");
    }

    public Test() {
         running = true;
         cleanupLock = new ReentrantLock();
         this.start();
         cleanup();
    }

    public static void main(String argv[]) {
        new Test();
    }
}

上面的代码是我写的

我预期的结果是:

running! (many times)
cleanup!

但是当我 运行 我得到了:

running! (many times)

而且没有停止。

所以,我在 while 语句中添加了 System.out.println(running);

按预期打印。

想知道为什么会这样。 很好奇如何修复它。

如果我们忘记了 running 未初始化为 true 这一事实(有问题已更新),从我正在阅读的内容和您观察到的内容来看,这是正常行为:

The result I expected was:

running! (many times)
cleanup!

but when I ran it I got:

running! (many times)

and it didn't stop.

对于下面的评论(和反对票),我强调粗体部分:根据您的观察,我可以说:

  • 您启动的线程拥有锁。
  • 主线程(调用您的 Thread)处于休眠状态,直到另一个线程释放其锁。来自 lock() 的 javadoc如果锁被另一个线程持有,则当前线程将因线程调度目的而被禁用并处于休眠状态,直到获得锁,此时锁保持计数设置为 1。
  • 因为主线程处于休眠状态,所以不会调用清理方法,running 也不会设置为 false。

我可以说它被阻止了,因为您观察到 cleanup 从未输出过。如果主线程获得了锁,则输出cleanup,因为锁在循环条件之前,所以不会输出running!

这是根据您的观察得出的,但是,确实有一个线程可能先于另一个线程获取锁。

因为我不知道你想达到什么目的,我只能告诉你应该在你的循环中有一个退出条件......并且你可以从外部线程(同步)更改退出条件只有当你不要阻止它。

此外,您应该使用 try/finally 进行锁定,尤其是在操作可能失败的情况下,因为它可能会通过保留不应该保留的锁定而无限期地阻塞:

  lock.lock();
  try {
    ...
  } finally {
    lock.unlock(); 
  }

它保持运行因为没有什么可以告诉启动线程主线程改变了变量。主线程的更改对其他线程不可见。

它之所以在您添加 println 时起作用,是因为两个线程都在控制台打印编写器上使用和同步,引入了使变量更新可见的先行关系。

您可以使 运行 变量可变,这将使更改在没有 println 的情况下可见。