[ Team LiB ] |
10.3 DeadlocksEnsuring that resources are used correctly between threads is easy in Java. Usually, it just takes the use of the synchronized keyword before a method. Because Java makes it seem so easy and painless to coordinate thread access to resources, the synchronized keyword tends to get used liberally. Up to and including Java 1.1, this was the approach taken even by Sun. You can still see in the earlier defined classes (e.g., java.util.Vector) that all methods that update instance variables are synchronized. From JDK 1.2, the engineers at Sun became more aware of performance and are now careful to avoid synchronizing willy-nilly. Instead, many classes are built unsynchronized but are provided with synchronized wrappers (see the later section Section 10.4.1). Synchronizing methods liberally may seem like good safe programming, but it is a sure recipe for reducing performance at best, and creating deadlocks at worst. The following Deadlock class illustrates the simplest form of a race condition leading to deadlock. Here, the class Deadlock is Runnable. The run( ) method just has a short half-second delay and then calls hello( ) on another Deadlock object. The problem comes from the combination of the following three factors:
The main( ) method accepts one optional parameter to set the delay in milliseconds between starting the two threads. With a parameter of 1000 (one second), there should be no deadlock. Table 10-1 summarizes what happens when the program runs without deadlock.
With a parameter of 0 (no delay between starting threads), there should be deadlock on all but the most heavily loaded systems. The calling sequence is shown in Table 10-2; Figure 10-2 summarizes the difference between the two cases. The critical difference between the deadlocked and nondeadlocked cases is whether d1Thread can acquire a lock on the d2 monitor before d2Thread manages to acquire a lock on d2 monitor.
A heavily loaded system can delay the startup of d2Thread enough that the behavior executes in the same way as the first sequence. This illustrates an important issue when dealing with threads: different system loads can expose problems in the application and also generate different performance profiles. The situation is typically the reverse of this example, with a race condition not showing deadlocks on lightly loaded systems, while a heavily loaded system alters the application behavior sufficiently to change thread interaction and cause deadlock. Bugs like this are extremely difficult to track down. The Deadlock class is defined as follows: package tuning.threads; public class Deadlock implements Runnable { String me; Deadlock other; public synchronized void hello( ) { //print out hello from this thread then sleep one second. System.out.println(me + " says hello"); try {Thread.sleep(1000);} catch (InterruptedException e) { } } public void init(String name, Deadlock friend) { //We have a name, and a reference to the other Deadlock object //so that we can call each other me = name; other = friend; } public static void main(String args[ ]) { //wait as long as the argument suggests (or use 20 ms as default) int wait = args.length = = 0 ? 20 : Integer.parseInt(args[0]); Deadlock d1 = new Deadlock( ); Deadlock d2 = new Deadlock( ); //make sure the Deadlock objects know each other d1.init("d1", d2); d2.init("d2", d1); Thread d1Thread = new Thread(d1); Thread d2Thread = new Thread(d2); //Start the first thread, then wait as long as //instructed before starting the other d1Thread.start( ); try {Thread.sleep(wait);} catch (InterruptedException e) { } d2Thread.start( ); } public synchronized void run( ) { //We say we're starting, then sleep half a second. System.out.println("Starting thread " + me); try {Thread.sleep(500);} catch (InterruptedException e) { } //Then we say we're calling the other guy's hello( ), and do so System.out.println("Calling hello from " + me + " to " + other.me); other.hello( ); System.out.println("Ending thread " + me); } } Figure 10-2. The difference between nondeadlocked and deadlocked execution |