Thread-Scheduling Scenarios

Chapter 7 - Concurrent Access to Objects and Variables

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

static synchronized Methods
In addition to the object -level lock that exists for each instance of a class, there is a class -level lock that all instances of a particular class share. Every class loaded by the VM has exactly one class-level lock. If a method is both static and synchronized , a thread must get exclusive access to the class-level lock before entering the method.
The class-level lock can be used to control concurrent access to static member variables. Just as the object-level lock was needed to prevent data corruption in non- static member variables, the class-level lock is needed to prevent corruption of static member variables. Even when no variables are involved, the synchronized modifier can be used on static methods simply to ensure that only one thread is inside the method at a time.
As you might guess from the name , the StaticNeedSync class in Listing 7.19 demonstrates a case where a static method would benefit from the addition of the synchronized modifier.
Listing 7.19  StaticNeedSync.javaDemonstrating the Need for Static Synchronized Methods
1: public class StaticNeedSync extends Object {
2:     private static int nextSerialNum = 10001;
3:
4:     public static int getNextSerialNum() {
5:         int sn = nextSerialNum;
6:
7:         // Simulate a delay that is possible if the thread
8:         // scheduler chooses to swap this thread off the
9:         // processor at this point. The delay is exaggerated
10:         // for demonstration purposes.
11:         try { Thread.sleep(1000); }
12:         catch (InterruptedException x) { }
13:
14:         nextSerialNum++;
15:         return sn;
16:     }
17:
18:     private static void print(String msg) {
19:         String threadName = Thread.currentThread().getName();
20:         System.out.println(threadName + : + msg);
21:     }
22:
23:     public static void main(String[] args) {
24:         try {
25:             Runnable r = new Runnable() {
26:                     public void run() {
27:                         print(getNextSerialNum()= +
28:                                 getNextSerialNum());
29:                     }
30:                 };
31:            
32:             Thread threadA = new Thread(r, threadA);
33:             threadA.start();
34:    
35:             Thread.sleep(1500);
36:    
37:             Thread threadB = new Thread(r, threadB);
38:             threadB.start();
39:    
40:             Thread.sleep(500);
41:
42:             Thread threadC = new Thread(r, threadC);
43:             threadC.start();
44:    
45:             Thread.sleep(2500);
46:
47:             Thread threadD = new Thread(r, threadD);
48:             threadD.start();
49:         } catch (InterruptedException x) {
50:             // ignore
51:         }
52:     }
53: }
The StaticNeedSync class has a private , static member variable, nextSerialNum , which is used to hold the next serial number that will be given out (line 2). The getNextSerialNum() method (lines 416) is both public and static . It is invoked when a unique serial number is needed. When called, it takes the current value of nextSerialNum and stores it in a local variable sn (line 5). Then, it puts the calling thread to sleep for one second to simulate the possibility that it could get swapped out at this point by the thread scheduler (line 11). When the thread gets a chance to run again, it increments the nextSerialNum member variable to prepare it for the next call (line 14). The locally stored serial number sn is returned to the caller (line 15).
The main thread starts four threads to interact with the getNextSerialNum() method. All four threads use the same Runnable (lines 2530). Inside this Runnable , the results of calling getNextSerialNum() are printed along with the thread name (lines 2728).
The main thread starts threadA (line 33) and then sleeps for 1.5 seconds. This is enough time for threadA to enter and return from getNextSerialNum() . Next, the main thread starts threadB (line 38) and then sleeps for 0.5 seconds (line 40) before starting threadC (line 43). Both threadB and threadC will be inside getNextSerialNum() at the same timeand this will cause some trouble.
After waiting 2.5 seconds (plenty of time for threadB and threadC to return), main starts threadD (lines 4548). threadD invokes getNextSerialNum() one last time. Listing 7.20 shows the output produced when StaticNeedSync is run. Your output should match.
Listing 7.20  Output from StaticNeedSync (Your Output Should Match)
threadA: getNextSerialNum()=10001
threadB: getNextSerialNum()=10002
threadC: getNextSerialNum()=10002
threadD: getNextSerialNum()=10004
When both threadB and threadC are allowed inside getNextSerialNum() at the same time, they both see 10002 for their number. Then each thread proceeds to increment the counter, and threadD sees 10004 . Two callers get the same serial number, and no one gets 10003 .
This code (without the sleep) is still vulnerable to duplicate serial numbers :
public static int getNextSerialNum() { // still dangerous
    int sn = nextSerialNum;
    nextSerialNum++;
    return sn;
}
Even this code has risks:
public static int getNextSerialNum() { // still dangerous
    return nextSerialNum++;
}
In both cases, a thread could be swapped out after reading the value of nextSerialNum but before incrementing it. This will happen only on rare occasions, but the risk should be eliminated.
StaticSync (see Listing 7.21) solves the problem by adding the synchronized method modifier (line 4) to the static method getNextSerialNum() . This ensures that only one thread is allowed into the method at a time and eliminates the problems of duplicate serial numbers.
Listing 7.21  StaticSync.javaUsing synchronized with static to Control Concurrent Access
1: public class StaticSync extends Object {
2:     private static int nextSerialNum = 10001;
3:
4:     public static synchronized int getNextSerialNum() {
5:         int sn = nextSerialNum;
6:
7:         // Simulate a delay that is possible if the thread
8:         // scheduler chooses to swap this thread off the
9:         // processor at this point. The delay is exaggerated
10:         // for demonstration purposes.
11:         try { Thread.sleep(1000); }
12:         catch (InterruptedException x) { }
13:
14:         nextSerialNum++;
15:         return sn;
16:     }
17:
18:     private static void print(String msg) {
19:         String threadName = Thread.currentThread().getName();
20:         System.out.println(threadName + : + msg);
21:     }
22:
23:     public static void main(String[] args) {
24:         try {
25:             Runnable r = new Runnable() {
26:                     public void run() {
27:                         print(getNextSerialNum()= +
28:                                 getNextSerialNum());
29:                     }
30:                 };
31:            
32:             Thread threadA = new Thread(r, threadA);
33:             threadA.start();
34:    
35:             Thread.sleep(1500);
36:    
37:             Thread threadB = new Thread(r, threadB);
38:             threadB.start();
39:    
40:             Thread.sleep(500);
41:
42:             Thread threadC = new Thread(r, threadC);
43:             threadC.start();
44:    
45:             Thread.sleep(2500);
46:
47:             Thread threadD = new Thread(r, threadD);
48:             threadD.start();
49:         } catch (InterruptedException x) {
50:             // ignore
51:         }
52:     }
53: }
Listing 7.22 shows the output produced when StaticSync is run. Your output should match.
Listing 7.22  Output from StaticSync (Your Output Should Match)
threadA: getNextSerialNum()=10001
threadB: getNextSerialNum()=10002
threadC: getNextSerialNum()=10003
threadD: getNextSerialNum()=10004
Note that this time, threadC gets the unique serial number of 10003 . The addition of synchronized to the static method getNextSerialNum() solves the problem by blocking threadC from entering until threadB was finished.

Toc


Java Thread Programming
Java Thread Programming
ISBN: 0672315858
EAN: 2147483647
Year: 2005
Pages: 149
Authors: Paul Hyde

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net