Java Swing EDT和并发

Whi*_*red 4 java concurrency swing invokelater event-dispatch-thread

我只是想知道是否仍然需要确保invokeLater()Runnable的同步性.

我遇到了死锁,需要在保持并发性的同时克服它.

这是一个好代码的例子吗?:

private String text;

private void updateText()
{
    SwingUtilities.invokeLater(new Runnable()
    {
        public void run()
        {
            synchronized(FrameImpl.this)
            {
                someLabel.setText(text);
            }
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

对于相当糟糕的示例感到抱歉,但我们必须假设它text被不同的线程修改,无法注入,并且依赖于正确的值.

这是正确的解决方案,还是通过将同步代码发送到未知的上下文中而无意中造成死锁问题?

谢谢.

Cam*_*ner 6

更好的解决方案是这样的:

public class Whatever {
    private String text;
    private final Object TEXT_LOCK = new Object();

    public void setText(final String newText) {
        synchronized (TEXT_LOCK) {
            text = newText;
        }
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                someLabel.setText(newText);
            }
        });
    }

    public String getText() {
        synchronized (TEXT_LOCK) {
            return text;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

这将确保如果两个线程尝试同时调用,setText那么它们将不会互相破坏.第一个线程将设置值的值,text并使用该值排列UI更新.第二个线程还将设置text第二个UI更新的值并排队.

最终结果是UI最终将显示最新的文本值,但内部text变量将立即包含最新的值.

几个笔记:

  1. 使用单独的锁定对象(即TEXT_LOCK)意味着您不会在其他地方锁定监视器的代码,Whatever并且无意中导致死锁.最好始终严格控制锁定物体.最好最小化同步块的大小.
  2. 可以使整个setText方法同步,但需要注意的是,它确实会使您像上面那样容易受到死锁的影响.
  3. 读取值text也需要同步,即使它Strings是不可变的.Java内存模型有一些细微之处,这意味着您总是需要围绕可以由多个线程读取/写入的变量进行同步.

查看Brian Goetz的Java Concurrency in Practice,深入了解并发的棘手部分(包括内存模型的怪异).