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 的情况下可见。
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 的情况下可见。