Java Swing EDT和并发

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

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

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

private String text; private void updateText() { SwingUtilities.invokeLater(new Runnable() { public void run() { synchronized(FrameImpl.this) { someLabel.setText(text); } } }); } 

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

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

谢谢。

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

 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; } } } 

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

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

几个笔记:

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

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

现在它是正确的,任务的所有输出必须包装在InvokeLater()中,另一个来自BackGround任务的更新GUI的例子在这里

 private String text; private void updateText() { synchronized (FrameImpl.this) { SwingUtilities.invokeLater(new Runnable() { public void run() { someLabel.setText(text); } }); } }