我有一个长期运行的Runnable。它在run()函数的while循环内执行大量迭代。我需要暂停和恢复可运行文件的功能,我使用了可以由另一个线程设置的 Volatile 布尔pauseFlag来实现该功能。

一旦Runnable检测到pauseFlagtrue,它将调用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 pauseFlagwait仍指的是原始对象。 (如注释中所指出的,通常只有两个布尔对象TRUEFALSE,这使得调试起来更加困难,因为您可能会偶然得到正确的对象)

因此,最好使用final引用,该引用在使用wait / notify时永远不会更改其基础对象。

08-26 02:00