Multithreading in Swing

An important compromise was made early in the design of Swing relating to speed, GUI consistency, and thread safety. To provide maximum performance and simplicity in the common case, Swing does not explicitly synchronize access to most Swing component methods. This means that most Swing components are, technically, not threadsafe for multithreaded applications. Now don’t panic: it’s not as bad as it sounds because there is a plan. All event processing in AWT/Swing is handled by a single system thread using a single system event queue. The queue serves two purposes. First, it eliminates thread safety issues by making all GUI modifications happen in a single thread. Second, the queue imposes a strict ordering of all activity in Swing. Because painting is handled in Swing using events, all screen updating is also ordered with respect to all event handling.

What this means for you is that multithreaded programs need to be careful about how they update Swing components after they are realized (added to a visible container). If you make arbitrary modifications to Swing components from your own threads, you run the risk of malformed rendering on the screen and inconsistent behavior.

There are several conditions under which it is always safe to modify a Swing component. First, Swing components can be modified before they are realized. The term realized originates from the days when the component would have created its peer object. It is the point when it is added to a visible container or when it is made visible in the case of a window. Most of our examples in this book set up GUI components in their main() method, add them to a JFrame, and then, as their final action, cause the JFrame to be displayed using setVisible(). This setup style is safe because components are not realized until the container is made visible. Actually, that last sentence is not entirely true. Technically, components can also be realized by the JFrame() pack() method. However, because no GUI is shown until the container is made visible, it is unlikely that any GUI activity can be mishandled.

Second, it’s safe to modify Swing components from code that is already running from the system event handler’s thread. Because all events are processed by the event queue, the methods of all Swing event listeners are normally invoked by the system event-handling thread. This means that event handler methods and, transitively, any methods called from those methods during the lifetime of that call, can freely modify Swing GUI components because they are already running in the system event-dispatching thread. If unsure of whether some bit of code will ever be called outside the normal event thread, you can use the static method SwingUtilities.isEventDispatchThread() to test the identity of the current thread. You can then perform your activity using the event-queue mechanism we’ll talk about next.

Finally, Swing components can be safely modified when the API documentation explicitly says that the method is threadsafe. Many important methods of Swing components are explicitly documented as threadsafe. These include the JComponent repaint() and revalidate() methods, many methods of Swing text components, and all listener add and remove methods.

If you can’t meet any of the requirements for thread safety listed previously, you can use a simple API to get the system event queue to perform arbitrary activities for you on the event-handling thread. This is accomplished using the invokeAndWait() or invokeLater() static methods of the javax.swing.SwingUtilities class:

public static void invokeLater(Runnable doRun)

Use this method to ask Swing to execute the run() method of the specified Runnable.

public static void invokeAndWait(Runnable doRun)throwsInterruptedException,InvocationTargetException

This method is just like invokeLater() except that it waits until the run() method has completed before returning.

You can put any activities you want inside a Runnable object and cause the event dispatcher to perform them using these methods. Often you’ll use an inner class; for example:

    SwingUtilities.invokeLater( new Runnable() {
        public void run() {
            MyComponent.setVisible(false);
        }
    } );

Java 7 introduced the SwingWorker class to assist in situations where you have a background process that needs to update a Swing UI after the process is complete or incrementally as it’s running. In the former case, it’s a simple matter of subclassing SwingWorker, and putting your long-running code in doInBackground() and your UI update code in done().

    package learning;

    import java.awt.BorderLayout;
    import java.awt.event.ActionEvent;
    import java.awt.event.ActionListener;
    
    import javax.swing.*;

    public class MysteryOfTheUniverse extends JFrame {
    
        JTextArea textArea;
        JButton solveButton;
        
        public MysteryOfTheUniverse() {
            super("Mystery of the Universe Solver");
            
            setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
            setSize(300, 300);
            
            textArea = new JTextArea();
            
            solveButton = new JButton("Solve Mystery");
            solveButton.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent ae) {
                    solveButton.setEnabled(false); 
                    solveMysteryOfTheUniverse();
                }
            });

            add(solveButton, BorderLayout.NORTH);
            add(new JScrollPane(textArea));
            add(new JButton("Click me! I'm not blocking."), 
                BorderLayout.SOUTH);
        }

        public void solveMysteryOfTheUniverse() {
            (new MysteryWorker()).execute();
        }

        class MysteryWorker extends SwingWorker<String, Object> {
           
            @Override
            public String doInBackground() {

                // Thinking for 4 seconds, but not blocking the UI
                try {
                    Thread.currentThread().sleep(4000);
                } catch (InterruptedException ignore) {}   

                solveButton.setEnabled(true); 

                return "Egg salad";
            }

            @Override
            protected void done() {
                try {
                    textArea.setText(get());
                } catch (Exception ignore) {}
            }
        }

        public static void main(String[] args) {
            new MysteryOfTheUniverse().setVisible(true);
        }
    }

When you click the Solve button, the application spends four seconds solving the mystery of the universe. If we weren’t using SwingWorker, the event dispatch thread would block, making the Click Me button at the bottom of the screen unclickable. Thanks to SwingWorker, the UI remains usable during the time the background task is executing. Don’t trust us. Try it!

SwingWorker can be used in more complex situations such as incremental updates of a progress bar. See SwingWorker’s JavaDoc introduction for an example of this usage.

You may find that you won’t have to use the event dispatcher or SwingWorker in simple GUI applications because most activity happens in response to user interface events where it is safe to modify components. However, consider these caveats when you create threads to perform long-running tasks such as loading data or communicating over the network.

Get Learning Java, 4th Edition now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.