Threads and Concurrent Programming
Threads may be seen as methods that execute at "the same time" as other methods. Normally, we think sequentially when writing a computer program. From this perspective, only one thing executes at a time. However, with today's multi-core processors, it is possible to literally have several things going on at the very same time while sharing the same memory. There are lots of ways that this is done in the real world, and this chapter goes over them in a way that you can apply to your own projects.
14.5 Using Threads to Improve Interface Responsiveness
Multithreaded Drawing: The Dotty Threa
One way to remedy this problem is to create a second thread (in addition to the GUI itself) to do the drawing. The drawing thread will be responsible just for drawing, while the GUI thread will be responsible for handling user actions in the interface.
The trick to making the user interface more responsive will be to interrupt the drawing thread periodically so that the GUI thread has a chance to handle any events that have occurred.
As Figure 14.14 illustrates,
the easiest way to convert Dotty into a thread is to have it implement the Runnable interface:
This version of Dotty will perform the same task as before except that
it will now run as a separate thread of execution. Note that its run()
method just calls the draw() method that we defined in the previous version. When the Dotty thread is started by the RandomDotGUI, we will
have a multithreaded program.
However, just because this program has two threads doesn’t necessarily
mean that it will be any more responsive to the user. There’s no guarantee
that the drawing thread will stop as soon as the Clear button is clicked. On most systems, if both threads have equal priority, the GUI thread won’t
run until the drawing thread finishes drawing all N dots.
Therefore, we have to modify our design in order to guarantee that the
GUI thread will get a chance to handle the user’s actions. One good way
to do this is to have Dotty sleep for a short instance after it draws each
dot. When a thread sleeps, any other threads that are waiting their turn the
will get a chance to run. If the GUI thread is waiting to handle the user’s click on Clear, it will now be able to call Dotty’s clear() method.
The new version of draw() is shown in Figure 14.15. In this version of
draw(), the thread sleeps for 1 millisecond on each iteration of the loop.
This will make it possible for the GUI to run on every iteration, so it will
handle user actions immediately.
Another necessary change is that once the clear() method is called,
the Dotty thread should stop running (drawing). The correct way to stop
a thread is to use some variable whose value will cause the run loop (or
in this case the drawing loop) to exit, so the new version of Dotty uses
the boolean variable isCleared to control when drawing is stopped.
Note that the variable is initialized to false and then set to true in the
clear() method. The for loop in draw() will exit when isCleared
becomes true. This causes the draw() method to return, which causes
the run() method to return, which causes the thread to stop in an orderly
Modifications to RandomDotGUI
We don’t need to make many changes in RandomDotGUI to get it to work with the new version of Dotty. The primary change comes in the actionPerformed() method. Each time the Draw button was clicked in the original version of this method, we created a dotty instance and then called its draw() method.
Figure 14.15: By implementing the Runnable interface, this version of Dotty can run as a separate thread.
In the revised version we must create a new Thread and pass it an instance of Dotty, which will then run as a separate thread:
Note that in addition to a reference to dotty we also have a reference to a
Thread named dottyThread. This additional variable must be declared
within the GUI.
Remember that when you call the start() method, it automatically
calls the thread’s run() method. When dottyThread starts to run, it
will immediately call the draw() method and start drawing dots. After
each dot is drawn, dottyThread will sleep for an instant.
Notice how the GUI stops the drawing thread. In the new version,
Dotty.clear() will set the isCleared variable, which will cause the
drawing loop to terminate. Once again, this is the proper way to stop a
thread. Thus, as soon as the user clicks the Clear button, the Dotty thread
will stop drawing and report its result.