A race condition occurs when two threads attempt to use the same resource at the same time. The following class demonstrates a simple race condition. Two threads simultaneously try to increment a counter. If each thread can complete the increment( ) method in its entirety without the other thread executing, then all is fine, and the counter monotonically increases. Otherwise, the thread context switcher has the opportunity to interrupt one thread in the middle of executing the increment( ) method and let the other thread run through this method. Note that the thread can actually be interrupted anywhere, not necessarily in the middle of the increment( ) method, but I've greatly increased the likelihood of an interruption in the increment( ) method by including a print statement there:
package tuning.threads; public class ThreadRace implements Runnable { //global counter static int num=0; public static void increment( ) { int n = num; //This next line gives the context switcher an ideal //place to switch context. System.out.print(num+" "); //And when it switches back, n will still be the old //value from the old thread. num = n + 1; } public static void main(String args[ ]) { ThreadRace d1 = new ThreadRace( ); ThreadRace d2 = new ThreadRace( ); Thread d1Thread = new Thread(d1); Thread d2Thread = new Thread(d2); d1Thread.start( ); d2Thread.start( ); } public void run( ) { for (int i = 200; i >= 0 ; i--) { increment( ); } } }
The output from executing this class on a single-processor test machine is:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
You see that after 16, the next number is 16 again, and after the first 32, the next number is 17, as the threads switch back and forth in the middle of the increment( ) method. On a multiprocessor machine, the situation is even more confused.
Synchronizing the increment( ) method ensures the correct behavior of a monotonically increasing counter, as this gives exactly the desired behavior: the method is forced to complete before another call to it from any thread can be started.
|