Zebby Dee
Zebby Dee

Reputation: 401

Modifying a Swing component based on PropertyChangeEvent from separate thread

If I understand correctly, when I modify a Swing component directly from another thread, that action should be placed on the EDT's event queue to prevent synchronization issues with the 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!");
            }
        });
    }
}

My question is, do I need to follow this same idiom when the Swing component is not modified directly within the non-EDT thread, but instead by a PropertyChangeListener executing on the EDT that receives a PropertyChangeEvent from another thread?

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!");
    }
}

It doesn't look like there's anything in PropertyChangeSupport that would make it inherently "Swing safe", but I don't want to clutter up my code with unnecessary calls to EventQueue.invokeLater() if they're not required.

Upvotes: 2

Views: 781

Answers (1)

MadProgrammer
MadProgrammer

Reputation: 347184

Only AWTEvent objects are processed from the context of the Event Dispatching Thread, all other types of events are generally raised manually (generally using a for-loop and a list of registered listeners).

This means, in the context of your example, the property change event would actually be triggered outside of the EDT. Because most Swing components assume that they are being notified within the EDT, this is indeed dangerous.

Now, you could modify any of your PropertyChangeListeners to check that they are being executed within the context of the EDT, but what you can't do is change how other registered listeners might work.

If you need to do this (I would question the reason's why), you should wrap the firePropertyChange in an invokeLater call to re-sync it back to the EDT.

Equally, you could use a SwingWorker and publish the changes so that they are processed within the EDT for you...

Upvotes: 2

Related Questions