How to Use Threads in Java Swing

Programming isn't easy, and adding a user interface around functionality can really make life difficult. Especially since not all UI frameworks are thread safe (including Swing). So how do we efficiently handle the UI, run the worker code, and communicate data between the two, all while keeping the UI responsive?

Luckily for users of Swing, there are a few options, both of which can make programming GUIs much simpler. Here are two of those options.

Invoke Later

SwingUtilities.invokeLater() is great for updating the UI from another thread. Maybe you have a long-running task, and you'd like to update a progress bar to provide feedback for the user. You could try something like this:

startButton.addActionListener(new ActionListener() {
  public void actionPerformed(ActionEvent e) {
    progressBar.setValue(0);
    progressBar.setStringPainted(true);

    // Runs outside of the Swing UI thread
    new Thread(new Runnable() {
      public void run() {
        for (i = 0; i <= 100; i++) {

          // Runs inside of the Swing UI thread
          SwingUtilities.invokeLater(new Runnable() {
            public void run() {
              progressBar.setValue(i);
            }
          });

          try {
            java.lang.Thread.sleep(100);
          }
          catch(Exception e) { }
        }
      }
    }).start();
  }
});

Hopefully you can see from this example how you might use SwingUtilities.invokeLater() in order to communicate between UI and worker threads. You can think of invokeLater as a simple callback to the UI for sending whatever updates you need.

Swing Worker

SwingWorker<T,V> can be used similarly to invokeLater, but each has its strong points. Personally, I prefer to use SwingWorker for long-running tasks that I don't need to update the UI for (like loading a large document), while invokeLater might be used more for long-running tasks that do need to update the UI. SwingWorker can be used in this way with:

private Document doc;

JButton button = new JButton("Open XML");
button.addActionListener(new ActionListener() {
 @Override
 public void actionPerformed(ActionEvent e) {

   // All code inside SwingWorker runs on a seperate thread
   SwingWorker<Document, Void> worker = new SwingWorker<Document, Void>() {
     @Override
     public Document doInBackground() {
       Document intDoc = loadXML();
       return intDoc;
     }
     @Override
     public void done() {
       try {
         doc = get();
       } catch (InterruptedException ex) {
         ex.printStackTrace();
       } catch (ExecutionException ex) {
         ex.printStackTrace();
       }
     }
   };

   // Call the SwingWorker from within the Swing thread
   worker.execute();
 }
});

This class breaks out worker events in to methods that can be implemented depending on your needs. For more advanced usage, check out the publish(V... chunks) and process(List<V> chunks) methods.

Resources