Knowing When a Thread Stops

Articles —> Knowing When a Thread Stops

In java, determining when a Thread has finished can get ugly real quick1. Passing references to the Thread of concrete objects wishing to be notified upon completion can lock the code into a particular design, making it hard to pull it apart at a later date and difficult to adapt, rewrite, reuse, and redesign later on.

The Observer Pattern in java can be used to design a method to decouple the thread from the client, making the design reusable and more amenable to change. This pattern allows a client to listen for particular events from another class, in our case to listen for when the thread is completed. Who will be the listener/observer? Ideally, whoever wants to be. Given this constraint (or lackthereof), we can write an interface that can be implemented by any class wishing to listen:


/**

 * An interface that can be used by the NotificationThread class to notify an

 * object that a thread has completed. 

 * @author Greg Cope

 */

public interface TaskListener {

	/**

	 * Notifies this object that the Runnable object has completed its work. 

	 * @param runner The runnable interface whose work has finished.

	 */

	public void threadComplete( Runnable runner );

}

Now that the listener is written, how can we use this interface to notify a thread has completed? One way is to create a class where these listeners can be added and/or removed, run the long task, then notify all listeners when the task is complete. This can be as simple as implementing the Runnable interface, then calling the listeners just prior to the method exiting:


/**

 * This abstract class implements the Runnable interface and can be used to notify listeners

 * when the runnable thread has completed. To use this class, first extend it and implement

 * the doRun function - the doRun function is where all work should be performed. Add any listener to update upon completion, then 

 * create a new thread with this new object and run. 

 * @author Greg Cope

 *

 */



public abstract class NotificationThread implements Runnable{

	

	/**

	 * An abstract function that children must implement. This function is where 

	 * all work - typically placed in the run of runnable - should be placed. 

	 */

	public abstract void doWork();

	

	/**

	 * Our list of listeners to be notified upon thread completion.

	 */

	private java.util.List<TaskListener> listeners = Collections.synchronizedList( new ArrayList<TaskListener>() );

	

	/**

	 * Adds a listener to this object. 

	 * @param listener Adds a new listener to this object. 

	 */

	public void addListener( TaskListener listener ){

		listeners.add(listener);

	}

	/**

	 * Removes a particular listener from this object, or does nothing if the listener

	 * is not registered. 

	 * @param listener The listener to remove. 

	 */

	public void removeListener( TaskListener listener ){

		listeners.remove(listener);

	}

	/**

	 * Notifies all listeners that the thread has completed.

	 */

	private final void notifyListeners() {

		synchronized ( listeners ){

			for (TaskListener listener : listeners) {

			  listener.threadComplete(this);

			}

		}

	}

	/**

	 * Implementation of the Runnable interface. This function first calls doRun(), then

	 * notifies all listeners of completion.

	 */

	public void run(){

		

		doWork();

		notifyListeners();

		

	}

}

In the code listing above, the long running task can be placed in the doWork method. When a new thread is created using this Runnable, the run method is called: first calling doWork, then updating all listeners.

When does this come in handy? In designing Swing user interfaces, longer running tasks can lock up the user interface during a long task. Placing these tasks into another thread prevents GUI lockup, but in many cases one wishes to update the user interface after the task has been completed2. This design allows the program to do so, and allows one to do so in a way in which the classes are decoupled from one another, making the code reusable and easy to adapt.

1For more information on threads, see Introduction to Java Threads

2Updating Swing components from another thread can be dangerous - be sure to place all updates into the event dispatch thread (see Threads and Swing for more information.



There are no comments on this article.

Back to Articles


© 2008-2022 Greg Cope