Java Lab 5: Multithreaded Fractal Explorer

This week we will finish off the Fractal Explorer with one more feature, the ability to draw the current fractal with multiple background threads. This will produce two big improvements: first, the user interface won't freeze while the new fractal is being drawn, and second, if your computer has multiple processors then your drawing will be much faster. Although multithreaded programming can be very challenging, it will be very easy to make this week's changes by leveraging Swing's built-in support for background threads.

So far, whether you have realized this or not, all of our fractal computation and drawing has been performed on Swing's Event-Dispatch Thread. This is the thread that handles all Swing events, such as button-clicks, redrawing, and so forth. This is why your user interface freezes when a fractal is being computed; since the computation is being performed on the event-dispatch thread, no events can be processed until the computation is completed.

This week we will change the program to use one or more background threads to compute the fractal. Specifically, the dispatch thread will not be used to compute the fractal. Now, if a computation is going to be performed by multiple threads, we need to be able to break it down into multiple independent parts. When drawing fractals, this is very easy to do - we can simply give each thread a single line of the fractal to compute. The harder part is making sure we follow the important Swing constraint that we only interact with Swing components from the event-dispatch thread, but fortunately Swing again provides tools to make this very easy.

It is in fact a very common situation in UI programming, that the user interface must trigger a long-running operation, but the operation should take place in the background so that the UI remains responsive. Web browsers are probably the most widely-used example of this; while a page is being downloaded and rendered, the user must be able to cancel the operation, or click on a link, or do any number of other things as well. To facilitate this kind of interaction, Swing provides the javax.swing.SwingWorker class, which makes it very easy to perform a task on a background thread. SwingWorker is an abstract class; Swing expects you to extend it, and provide the functionality to perform the background task. The most important methods to implement are:

The SwingWorker class has a confusing specification, it is actually SwingWorker<T, V>. The type T is the type of the value returned by doInBackground(), when the entire task is completed. The type V is used when a background task returns intermediate values as it runs; these intermediate values would be exposed by the publish() and process() methods. It is not always necessary to use either or both of these types; in these cases, we can simply specify Object for the unused type(s).

Drawing in the Background

You will be working primarily within the FractalExplorer class this week. Some of the code will be new, but some of it will actually be refactoring code that you have already written.

Once you have this functionality completed and debugged, you should now have a much faster and more responsive user interface. If you happen to have multiple cores, you will definitely see a substantial improvement.

You will notice one issue with your user interface - if you click on the screen or on a button during a redraw, the program will process it even though it should probably be ignored until the operation is completed. Fortunately this is pretty straightforward to fix.

Ignoring Events During Redraw

The easiest way to solve the issue of ignoring events during redraw is to keep track of how many rows are remaining to be completed, and ignore or disable user interactions until all rows are drawn. This has to be done very carefully though, or else we will have some very nasty bugs. What we can do is this: add a "rows remaining" field to our Fractal Explorer class, and use it to know when a redraw is completed. We will only read and write this value from the event dispatch thread so that we never introduce any concurrent accesses. If we only interact with a resource from a single thread, we won't have any concurrency bugs. Here is what you need to do:

Once you have completed these steps, you should have a nice, solid fractal display program that can draw fractals with multiple threads, and that won't allow users to do anything while the rendering process occurs in the background. Pretty neat.

All Done!

Once your program is operating correctly, and you have properly commented all of your code, submit your work on csman.


Updated May 10, 2011. Copyright (C) 2011 Caltech.