Concurrency in Java Swing

Java Swing Programming is mainly used for developing GUI for desktop applications. This blog aims to provide a brief understanding of `Concurrency in Swing Programming’.

Uses of concurrency in Swing

To create responsive UI, Swing employs threads.

The types of threads that Swing deals with are:

  • Initial Threads – Threads which execute initial application code. This is where the application logic begins.
  • Event Dispatch Thread (EDT) – Threads where all event handling code is executed. Event handling code is the major part of code in Swing programming that interacts with Swing framework.
  • Worker Threads / Background Threads – Threads where time consuming background tasks (long-running tasks) are executed.

We do not need to explicitly provide code for creating these threads; Swing framework or run time provides them. We can just utilize these threads to create a responsive Swing program.

Initial Threads

In Swing programming, initial threads create a Runnable object that initializes the GUI and schedules that object for execution on the Event Dispatch thread. If the GUI is once created, the program is mainly driven by GUI events.

An initial thread schedules the GUI creation task by invoking javax.swing.SwingUtilities.invokeLater or javax.swing.SwingUtilities.invokeAndWait. Both  these methods take a single argument; the Runnable defines the new task. invokeLater simply schedules the task and returns; invokeAndWait waits for the task to finish before returning.

Example:

SwingUtilities.invokeLater(new Runnable() {
 @Override
 public void run() {
 new MainFrame("SwingWorker Demo");
 }
 });

SwingUtilities.invokeAndWait(new Runnable(){
@Override
public void run(){
new MainFrame(“InvokeAndWait Example”);
}
});

Event Dispatch Thread

Almost all code that creates or interacts with Swing components must run on the event dispatch thread. Swing Event handling code, runs on this special thread. Most code that invokes Swing methods also runs on this thread. This is needed because most Swing object methods are not ‘thread safe’. In API specification, some Swing component methods are labelled ‘thread safe’; these can be safely invoked from any thread. All other Swing component methods must be invoked from the event dispatch thread.

Code running on the event, dispatches thread as a series of short tasks. Most tasks are invocations of event-handling methods, such as ActionListener.actionPerformed. Other tasks can be scheduled by application code, using invokeLater or invokeAndWait. Tasks on the event dispatch thread must finish quickly; otherwise, unhandled events back up and the UI becomes unresponsive.

javax.swing.SwingUtilities.isEventDispatchThread is used to determine whether our code is running the event dispatch thread.

Example:

private JLabel countLabel1 = new JLabel("0");
 private JButton startButton = new JButton("Start");

startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
start();
}
});

private void start() {
Thread worker = new Thread(){
public void run(){
for(int i=0; i<=10; i++) {
countLabel1.setText(Integer.toString(i));
try {
Thread.sleep(1000);
} catch (InterruptedException e) {

}
}
statusLabel.setText(“Completed.”);
System.out.println(“Invoke and Wait…”);
}
};
worker.start();
}


Worker Threads and SwingWorker

Worker threads are also known as background threads. For executing long-running tasks, Swing program uses worker threads.

Tasks that run on Worker threads are created using javax.swing.SwingWorker. Each task running on a worker thread is represented by an instance of this class. Communication and coordination between worker thread tasks and the tasks on other threads are featured by this class.

SwingWorker is an abstract class; we must define a subclass in order to create a SwingWorker object; anonymous inner classes are often useful for creating very simple SwingWorker objects.

Important point to note: For each new background task, a new instance of javax.swing.SwingWorker is needed. The instances are not reusable.

Communication & Control features of SwingWorker

  • The SwingWorker subclass defines a method, done, which is automatically invoked on the event dispatch thread when the background task is finished.
  • SwingWorker implements util.concurrent.Future. This interface allows the background task to provide a return value to the other thread. Other methods in this interface allow cancellation of the background task and finding whether the background task has finished or been cancelled.
  • Intermediate results can be provided by the background task by invoking publish, which causes SwingWorker.process to be invoked from the event dispatch thread.

All concrete subclasses of SwingWorker implement doInBackground; implementation of done is optional.

SwingWorker is a generic class, with two type parameters. The first type parameter specifies a return type for doInBackground, and also for the get method, which is invoked by other threads to retrieve the object returned by doInBackground. SwingWorker‘s second type parameter specifies a type for interim results returned while the background task is still active. Void can be used as a placeholder, if our code doesn’t return any interim results.

Example:

private void start() {
 //anonymous class for creating object/instance of SwingWorker class.
 SwingWorker<Void, Void> worker2 = new SwingWorker<Void, Void>(){
 @Override
 protected Void doInBackground() throws Exception {
 for (int i = 0; i <= 10; i++) {
 Thread.sleep(1000);
 System.out.println("Running " + i);
 }

return null;
}

@Override
public void done(){
System.out.println(“Done!!”);
}
};
worker2.execute();
}

Cancelling Background Tasks

SwingWorker.cancel can be invoked to cancel a running background task. There are two ways we can do this.

  • By terminating when it receives an interrupt.
  • By invoking the status method isCancelled at short intervals. This method returns true if cancel has been invoked for this SwingWorker.

The cancel method takes a single boolean argument. If the argument is true, cancel sends the background task an interrupt. Whether the argument is true or false, invoking cancel changes the cancellation status of the object to true. The cancellation status cannot be changed back, once changed.

java.util.concurrent.CancellationException is thrown, if get is invoked on a SwingWorker object after its background task has been cancelled.

Status Methods

Status methods report on the status of the background task.

  • isCancelled – returns true if the task has been cancelled.
  • isDone – returns true if the task has finished either normally or by being cancelled.

Hope this blog gave you some basic ideas on Concurrency used in Swing. Thanks for reading!

Author

  • Madhunika works as Senior Software Engineer with Trigent Software. She has strong experience in Core Java, J2EE, PL/SQL, Web technologies and is interested in exploring new Java-based technologies.