Recommended: Sing it, brah! 5 fabulous songs for developers
JW's Top 5
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 4 of 5
Don't be misled by the fact that you're using a Runnable object to pass a request to Swing. The run() method doesn't run on its own thread; it uses the Swing event queue. If run() takes a long time to execute, your program's user interface will appear to lock up -- it won't respond at all to any user
input. It's best for these methods either to be very short or to fire up their own threads to do time-consuming background
operations.
One common situation where a standalone thread needs to talk to Swing is an animation loop. Listing 1 below shows how you
might be tempted to implement an animation loop. This is typical AWT-style code, where the run() method of a thread (line 42) forces a repaint at a regular interval. The thread is fired up in start() (line 23). Unfortunately, this code only appears to work -- it will probably run fine until you try to actually use an Animator1 object in a real application that uses Swing intensively, then it will blow up. The run() method on line 42 of Listing 1 runs on the thread created on line 24, not on the Swing thread. Consequently, the call to repaint on line 51 is not safe. We're calling an unsynchronized Swing method from somewhere other than a listener method or the Swing event
processing thread.
Also, the animation loop is a bit jerky because the repaint doesn't actually happen on a fixed interval. There's a difference
between repainting every 100 milliseconds and waiting 100 milliseconds between repaint requests. In the latter case, which
is what I'm doing here, the actual time between refreshes can vary, depending on how long it takes for repaint() to execute. That's not really the behavior I want.
|
Killing threads
You should pay attention to how run() terminates in Listing 1. Jeff Richter, in his book, Advanced Windows Programming, says that there are three ways in which threads can die: they can die a natural death, commit suicide, or be murdered! Java's
stop() method, use of which falls in both the murder and suicide category, is now deprecated because it can leave NT DLLs in an
unstable state, so "murder" and "suicide" aren't options. The remaining option, a "natural death," is brought about by returning
from run(). If you want to shut a thread down, you have to ask the thread to shut itself down.
A simple approach might simply call a method that sets an internal flag that's tested in the main run() loop of the thread. This approach doesn't usually work, however, because most threads spend the bulk of their time blocked,
waiting for some operation like a blocking read to complete. A suspended thread can't test a flag. However, sending a blocked
thread an interrupt() message will bump it out of the wait (with an exception toss). The stop() method (line 28) does just that: it sends an interrupt() message to the driver thread in order to stop it. (Remember, stop() itself runs on whatever thread calls it, not on the animator thread. The only methods that run on the animator thread are
the ones that Animator.run() calls.) Since I intend for an interrupt operation to kill the thread, the run() method (line 42) catches the exception and ignores it.
What if the thread is interrupted, rather than blocked, while it's working? The Thread class provides two methods to check whether or not a thread has been interrupted. Thread.interrupted(), which I've used here, returns true if the current thread has been interrupted. A call to some_thread.isInterrupted() returns true if some_thread has been interrupted. It's a good idea to call one or the other of these periodically as the thread works.
Unit tests
The final thing to look at in Listing 1 is the unit-test class: Test, on line 57. A unit test is a piece of code that tests one thing, in this case a class. It's a good idea to associate a unit test with every class
you write, and there are several ways to do that. You could pair a test file with every class file, but you'd end up with
too many files to maintain if you did that, and keeping the test routine in phase with the class as it evolves would be difficult
since you could recompile the class without recompiling the test.
Another strategy is to put a main() in every class. I don't do that because I don't want to carry around the baggage of a unit test in every class file that
comprises the program. The tests are often bigger than the class I'm testing, and I don't want to inflate the application
size with all this test code.
A third option -- putting the test code in a separate class, but in the same file as the class being tested -- doesn't work out very well because the package-level name space becomes corrupted with all the names of the test classes. Just managing these names can become a big problem.
com.holub.asynch package) is available in the "Goodies" section on my Web site, http://www.holub.com. The version on the Web site should be considered the definitive version -- at least it corrects any bugs I know about.
Timer class in the JDK docs and at http://java.sun.com/products/jdk/1.2/docs/api/javax/swing/Timer.html