4

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

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

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

private String text;

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

抱歉这个相当糟糕的例子,但我们必须假设它text正在被不同的线程修改,不能被注入,并且依赖于正确的值。

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

谢谢。

4

2 回答 2

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

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

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

几点注意事项:

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

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

于 2011-07-27T04:46:35.847 回答
-2

现在它是正确的,任务的所有输出都必须包装到 InvokeLater(),另一个从 BackGround 任务更新 GUI 的示例在这里

private String text;

private void updateText() {

    synchronized (FrameImpl.this) {
        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                someLabel.setText(text);
            }
        });
    }
}
于 2011-07-27T04:28:12.433 回答