Clicky

ChrisEllsworth.com

The online home of Christopher Chess Ellsworth
Welcome to ChrisEllsworth.com Sign in | Join | Help
in Search

Chris's Development Blog

Tutorials and thoughts on software development.

Java Concurrency API Example

Download the example code.

I was asked to put together some teaching materials for a Data Structures class as the second part of their introduction to threading.  I decided to go ahead and jump right in to the Java Concurrency API and needed a simple example to illustrate the concepts.  I adapted this from an example in their textbook (not sure about the authors name), although no code actually remains from the author.  It is a problem domain that they where already familiar with, animated sorting algorithms.

The idea is that there is a set of data that needs to be sorted.  A user interface displays this data while it is being sorted by some sorting algorithm running in a separate thread (i.e. two threads running concurrently with shared data).  This raises the question of data protection and synchronization.  This also provides a way to illustrate the benefit of separating presentation logic from application logic.  Here is what the application looks like (sorting data on the left, sort completed on the right):

 

 

At the core of the example are the classes SortAlgo and SortDisplay, which are intended to share a common instance of the Data.  In order to have the ability to compare sorting techniques, the sorting algorithm's Call() method times how long it takes to sort the data and returns the sort time once sorting has completed.  The SortDisplay's Run() method continuously updates (redraws) the data display until the SortCompleted(Long sortTime) method is invoked, at which point the Run() method terminates and the display is updated to include the sort time.

 

 

The Callable<T> and Runnable interfaces are used to indicate that a class has some task(s) that needs to be performed in a separate thread.  Runnable,  around since the early days of Java, states that the implementing class must define a method with the signature public void run().  This method commonly has some time consuming task to perform and then returns or runs continuously, waiting for data to become available, processing it, and then waiting for then next set of data to arrive.  Either way, the run() method typically only modifies data related to the class for which it is defined.

Callable<T>, on the other hand, is intended for time consuming tasks that return a result.  The sorting algorithm is a perfect example of this kind of task.  It sorts the data (time consuming) and then returns the time it took to sort.  Lets look at what happens if we execute the following code:

 

SortAlgo sortAlgo = new SortAlgo(data);
Long time = sortAlgo.call();
System.out.println("Sort Completed: "+time);

 

Once we call the sortAlgo.call() method, we are stuck waiting until the sorting completes before we can do anything else.  Because we are implementing the Callable<T> interface, we can rewrite this using the Concurrency API to allow us to do other work while we are waiting for the sorting to complete.  

 
ExecutorService taskExecutor = Executors.newCachedThreadPool();
CompletionService<Long> taskCompletionService = 
    new ExecutorCompletionService<Long>(taskExecutor);

SortAlgo sortAlgo = new SortAlgo(data);
Future<Long> sortAlgoFuture = taskCompletionService.submit(sortAlgo); 

while(!sortAlgoFuture.isDone()) {
    // Do some work...
    System.out.println("Working on something...");
}

// Sorting is now complete, so lets get the result
Long time = sortAlgoFuture.get();
System.out.println("Sort Completed: "+time);

 

This time, we are never stuck waiting for the task to complete.  That is we can be doing something useful at all times until the data is sorted, at which point we can immediately get the result (the sort time) and move on.  A common use of this kind of API is to maintain a collection of tasks that are being performed and remove them one-by-one as they are completed.

Download the code and examine the interactions between the SortAlgo and SortDisplay classes.  Thread synchronization (data protection) is attained through the use of the Lock class.  The students in our Data Structures course where assigned the task of taking this code and extending it to handle multiple sorting algorithms (think inheritance and abstract classes) and to be able to dynamically wire up multiple data, sorting algorithms, and sort displays from a GUI.  This is a good exercise if you wish to gain a better understanding of the Java Concurrency API.  Also, examine the short FutureCallback<T> class in the example archive, which combines reflection and the concurrency API to produce a very handy utility class.

Published Tuesday, October 31, 2006 5:01 PM by Christopher Chess Ellsworth
Filed under: ,
New Comments to this post are disabled

Locations of Visitors

Locations of visitors to this page
©2007 Christopher Chess Ellsworth