我有一个长期运行的Runnable。它在run()函数中的while循环内执行大量迭代。我需要暂停和恢复runnable的功能,我使用可以由另一个线程设置的volatile pauseFlag
实现。
Runnable检测到pauseFlag
为true
后,会调用pauseFlag.wait()
暂停执行。通过将pauseFlag
设置为false
然后调用pauseFlag.notifyAll()
来完成恢复。
所以pauseFlag
都充当了旗帜和互斥体。但是,这种组合功能不起作用。 Runnable无限期地阻止pauseFlag.wait()
。
如果我创建一个单独的互斥锁,比如Object mutex = new Object();
并使用mutex.notifyAll()
/ mutex.wait()
,同时仍然使用pauseFlag
作为布尔标志,则Runnable 会执行按预期行事。
非工作代码如下所示:
public class PausableRunnable implements Runnable
{
private boolean done;
private volatile Boolean pauseFlag = false;
/** Pause execution. This is an asynchronous (non-blocking) call. */
public void pause() // <-- called by another thread
{
pauseFlag = true;
}
/** Resume execution */
public void resume() // <-- called by another thread
{
pauseFlag = false;
synchronized (pauseFlag)
{
pauseFlag.notifyAll();
}
}
@Override
public void run()
{
try
{
while (!done && !Thread.currentThread().isInterrupted())
{
while (pauseFlag)
{
synchronized (pauseFlag)
{
// Pause flag was set. Suspend until we are notified that we can continue
pauseFlag.wait();
}
}
// execute our main behaviour. set done = true when done iterating.
// ....
}
} catch (InterruptedException e)
{
Thread.currentThread().interrupt();
}
}
}
所以,虽然我通过使用单独的对象找到了解决方案,但我想了解这个问题。为什么上述实现不起作用?
答案 0 :(得分:8)
我曾犯过同样的错误。
wait
/ notify
适用于对象,而非引用
更改
引用的对象时 private volatile Boolean pauseFlag
wait
仍然指的是原始对象。 (正如评论中指出的那样,通常只有两个布尔对象,TRUE
和FALSE
,这使得调试更加困难,因为你可能偶然得到正确的一个)
因此,在使用wait / notify时,最好使用永远不会更改其基础对象的final
引用。