【问题标题】:Modifying a Swing component based on PropertyChangeEvent from separate thread从单独的线程修改基于 PropertyChangeEvent 的 Swing 组件
【发布时间】:2013-09-14 20:59:20
【问题描述】:

如果我理解正确,当我直接从另一个线程修改 Swing 组件时,该操作应该放在 EDT 的事件队列中以防止与 GUI 的同步问题:

public class SwingFrame extends JFrame {

    private JTextField _textField;

    public SwingFrame() {
        _textField = new JTextField();
        Thread thread = new Thread(new SomeRunnable(_textField));
        thread.start();
    }
}

public class SomeRunnable implements Runnable {

    private final JTextField _textField;

    public SomeRunnable(final JTextField textField) {
        _textField = textField;
    }
    @Override
    public void run() {
        // _textField.setText("Goodbye, Swing!"); /* wrong */
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                _textField.setText("Hello, Swing!");
            }
        });
    }
}

我的问题是,当 Swing 组件不是在非 EDT 线程中直接修改,而是通过在 EDT 上执行的 PropertyChangeListener 从另一个线程接收 PropertyChangeEvent 时,我是否需要遵循相同的习惯用法?

public class SwingFrame extends JFrame implements PropertyChangeListener {

    private JTextField _textField;

    public SwingFrame() {
        _textField = new JTextField();
        Thread thread = new Thread(new SomeRunnable());
        thread.start();
    }

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        if (evt.getPropertyName().equals("text")) {
            _textField.setText(String.valueOf(evt.getNewValue()));
        }
    }
}

public class SomeRunnable implements Runnable {

    private final PropertyChangeSupport _propertyChangeSupport;

    public SomeRunnable() {
        _propertyChangeSupport = new PropertyChangeSupport(this);
    }
    @Override
    public void run() {
        // Ok? Or wrap in EventQueue.invokeLater()?
      _propertyChangeSupport.firePropertyChange("text", null, "Hello, Swing!");
    }
}

PropertyChangeSupport 中似乎没有任何东西可以使其本质上“Swing 安全”,但如果不需要的话,我不想通过对 EventQueue.invokeLater() 的不必要调用来混淆我的代码。

【问题讨论】:

  • @nachokk 我能理解你的来历 - 包装直接访问 Swing 组件的代码基本上会使其与第一个示例相同。但我的问题更多地与是否有必要包装 either 调用有关,如果是,为什么或为什么不?
  • “相信” setText 是您可以在 Swing 中进行的少数线程安全调用之一,但这需要验证。我总是谨慎行事,并在 EDT 内做所有事情,这更容易记住
  • @MadProgrammer 我用setText 作为SSCCE 的例子,没想到,哎呀——我实际上是在尝试举例说明非线程安全的操作。
  • 正如我所说的“我相信”,这并不是事实;)

标签: java multithreading swing


【解决方案1】:

只有AWTEvent 对象是从事件调度线程的上下文中处理的,所有其他类型的事件通常是手动引发的(通常使用for-loop 和注册的侦听器列表)。

这意味着,在您的示例上下文中,属性更改事件实际上会在 EDT 之外触发。因为大多数 Swing 组件都假定它们是在 EDT 中得到通知的,所以这确实很危险。

现在,您可以修改您的任何 PropertyChangeListeners 以检查它们是否在 EDT 的上下文中执行,但您不能做的是更改其他已注册侦听器的可能方式工作。

如果您需要这样做(我会质疑原因),您应该将 firePropertyChange 包装在 invokeLater 调用中以将其重新同步回 EDT。

同样,您可以使用 SwingWorkerpublish 更改,以便它们在 EDT 中为您提供 processed...

【讨论】:

  • 很好的解释,谢谢。我刚刚查看了SwingWorker 处理此问题的方式,看起来实现与您建议的类似 - 它将PropertyChangeSupport 扩展为一个新类,检查它是否在 EDT 上执行,如果没有,则将firePropertyChange 方法包装在invokeLater 中。
  • 我认为 SwingWorker 是规则的例外
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多