我有一个长期运行的Runnable。它在run()函数的while循环内执行大量迭代。我需要暂停和恢复可运行文件的功能,我使用了可以由另一个线程设置的 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();
}
}
}
因此,尽管我通过使用单独的对象找到了解决方案,但我想了解这个问题。上面的实现为何不起作用?
最佳答案
我曾经犯过同样的错误。wait
/ notify
适用于对象,而不是引用
当您更改由引用的对象时private volatile Boolean pauseFlag
wait
仍指的是原始对象。 (如注释中所指出的,通常只有两个布尔对象TRUE
和FALSE
,这使得调试起来更加困难,因为您可能会偶然得到正确的对象)
因此,最好使用final
引用,该引用在使用wait / notify时永远不会更改其基础对象。