Closing a Stream to Break Out of the Blocked State

Chapter 17 - The BooleanLock Utility

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Noticing Brief Changes in Value Using TransitionDetector
The BooleanLock class is very useful when you need to know that the value inside has been changed and remains changed . As an example, consider this situation where there is an instance of BooleanLock
BooleanLock bl = new BooleanLock(false);
and you have two threads, threadA and threadB , each simultaneously running this code:
synchronized (bl) {
    bl.waitUntilTrue();
    bl.setValue(false);
}
Both threadA and threadB are blocked waiting inside the waitUntilTrue() method of BooleanLock . If a third thread, threadC , comes along and sets the value to true , both threadA and threadB are notified and compete to reacquire the lock before returning from wait() inside waitUntilTrue() . In this case, lets say that threadA reacquires the lock first, returns from wait() , returns from waitUntilTrue , and then invokes setValue() right away, passing in false . This all occurs before threadB returns from wait() because the synchronized statement block ensures that the object-level lock on bl is held until setValue() completes.
After threadA has set the value of bl back to false and left the synchronized block, threadB is able to reacquire the lock. threadB reacquires the lock, returns from the wait() inside waitUntilTrue() , and sees that the value is not true . Because the value is not true , threadB invokes wait() again and does not return from waitUntilTrue() .
In many cases, this is the type of behavior that you need. However, there are times when youll want your code to detect that the value changed from false to true even if the value is no longer true . BooleanLock does not provide this kind of information. You can used a class like TransitionDetector (see Listing 17.8) when you need to know that a change occurredno matter how briefly the value remained in the new state.
Listing 17.8  TransitionDetector.javaSensing All the Changes in Value
1: public class TransitionDetector extends Object {
2:     private boolean value;
3:     private Object valueLock;
4:     private Object falseToTrueLock;
5:     private Object trueToFalseLock;
6:
7:     public TransitionDetector(boolean initialValue) {
8:         value = initialValue;
9:         valueLock = new Object();
10:         falseToTrueLock = new Object();
11:         trueToFalseLock = new Object();
12:     }
13:
14:     public void setValue(boolean newValue) {
15:         synchronized (valueLock) {
16:             if (newValue != value) {
17:                 value = newValue;
18:                
19:                 if (value) {
20:                     notifyFalseToTrueWaiters();
21:                 } else {
22:                     notifyTrueToFalseWaiters();
23:                 }
24:             }
25:         }
26:     }
27:
28:     public void pulseValue() {
29:         // Sync on valueLock to be sure that no other threads
30:         // get into setValue() between these two setValue()
31:         // calls.
32:         synchronized (valueLock) {
33:             setValue(!value);
34:             setValue(!value);
35:         }
36:     }
37:
38:     public boolean isTrue() {
39:         synchronized (valueLock) {
40:             return value;
41:         }
42:     }
43:
44:     public void waitForFalseToTrueTransition()
45:             throws InterruptedException {
46:
47:         synchronized (falseToTrueLock) {
48:             falseToTrueLock.wait();
49:         }
50:     }
51:
52:     private void notifyFalseToTrueWaiters() {
53:         synchronized (falseToTrueLock) {
54:             falseToTrueLock.notifyAll();
55:         }
56:     }
57:
58:     public void waitForTrueToFalseTransition()
59:             throws InterruptedException {
60:
61:         synchronized (trueToFalseLock) {
62:             trueToFalseLock.wait();
63:         }
64:     }
65:
66:     private void notifyTrueToFalseWaiters() {
67:         synchronized (trueToFalseLock) {
68:             trueToFalseLock.notifyAll();
69:         }
70:     }
71:
72:     public String toString() {
73:         return String.valueOf(isTrue());
74:     }
75: }
TransitionDetector maintains three locks to control access and communication about the boolean value it encapsulates. In the constructor (lines 712), an initial value for value is passed in and stored. The three objects used for locking and inter-thread communication are created (lines 911). The valueLock object is used to control simultaneous access to value . The falseToTrueLock facilitates the use of the wait/notify mechanism to notify threads waiting for value to transition from false to true . The trueToFalseLock object is used to notify threads waiting for value to change from true to false .
Threads that want to know when value changes from false to true invoke the waitForFalseToTrueTransition() method (lines 4450). Inside, the invoking thread simply waits on the falseToTrueLock for notification (line 48). On the other hand, threads that want to know when value changes from true to false invoke the waitForTrueToFalseTransition() method (lines 5864). The threads that invoke this method simply wait on the trueToFalseLock for notification (line 62).
Inside setValue() (lines 1426), exclusive access to value is controlled by synchronizing on valueLock (line 15). If the new value is indeed different than the current value (line 16), value is changed (line 17). Depending on which transition occurred, one of the two methods notifyFalseToTrueWaiters() or notifyTrueToFalseWaiters() is invoked to signal any threads waiting for that particular change (lines 1923).
The private method notifyFalseToTrueWaiters() (lines 5256) is only called from within setValue() while the lock is still held on valueLock . Inside, any and all threads waiting on falseToTrueLock are notified (line 54). The notifyTrueToFalseWaiters() method (lines 6670) works similarly, but instead notifies threads waiting on the trueToFalseLock (line 68).
The pulseValue() method (lines 2836) is used to momentarily change value . If value is false , it is changed to true and then changed right back to false . Likewise, if value is true , it is changed to false and immediately back to true . Both calls to setValue() occur inside a synchronized block to ensure that no other thread can get a lock on valueLock while between the calls (lines 3235). Calling pulseValue() has the effect of notifying both the threads waiting inside waitForTrueToFalseTransition() and the threads waiting inside waitForFalseToTrueTransition() .
TransitionDetectorMain (Listing 17.9) demonstrates how TransitionDetector can be used.
Listing 17.9  TransitionDetectorMain.javaDemonstrating the Use of TransitionDetector
1: public class TransitionDetectorMain extends Object {
2:     private static Thread startTrueWaiter(
3:                 final TransitionDetector td,
4:                 String name
5:            ) {
6:
7:         Runnable r = new Runnable() {
8:                 public void run() {
9:                     try {
10:                         while (true) {
11:                             print(about to wait for false-to- +
12:                                 true transition, td= + td);
13:
14:                             td. waitForFalseToTrueTransition ();
15:
16:                             print(just noticed for false-to- +
17:                                 true transition, td= + td);
18:                         }
19:                     } catch (InterruptedException ix) {
20:                         return;
21:                     }
22:                 }
23:             };
24:
25:         Thread t = new Thread(r, name);
26:         t.start();
27:
28:         return t;
29:     }
30:
31:     private static Thread startFalseWaiter(
32:                 final TransitionDetector td,
33:                 String name
34:            ) {
35:
36:         Runnable r = new Runnable() {
37:                 public void run() {
38:                     try {
39:                         while (true) {
40:                             print(about to wait for true-to- +
41:                                 false transition, td= + td);
42:
43:                             td. waitForTrueToFalseTransition ();
44:
45:                             print(just noticed for true-to- +
46:                                 false transition, td= + td);
47:                         }
48:                     } catch (InterruptedException ix) {
49:                         return;
50:                     }
51:                 }
52:             };
53:
54:         Thread t = new Thread(r, name);
55:         t.start();
56:
57:         return t;
58:     }
59:
60:     private static void print(String msg) {
61:         String name = Thread.currentThread().getName();
62:         System.err.println(name + : + msg);
63:     }
64:
65:     public static void main(String[] args) {
66:         try {
67:             TransitionDetector td =
68:                     new TransitionDetector(false);
69:
70:             Thread threadA = startTrueWaiter(td, threadA);
71:             Thread threadB = startFalseWaiter(td, threadB);
72:
73:             Thread.sleep(200);
74:             print(td= + td + , about to set to ˜false);
75:             td.setValue(false);
76:
77:             Thread.sleep(200);
78:             print(td= + td + , about to set to ˜true);
79:             td.setValue(true);
80:
81:             Thread.sleep(200);
82:             print(td= + td + , about to pulse value);
83:             td.pulseValue();
84:
85:             Thread.sleep(200);
86:             threadA.interrupt();
87:             threadB.interrupt();
88:         } catch (InterruptedException x) {
89:             x.printStackTrace();
90:         }
91:     }
92: }
Inside main() (lines 6591), an instance of TransitionDetector is constructed (line 68) and two threads are started to wait for transitions. After sleeping for a short period, the main thread invokes setValue() , passing in false (lines 7375). 200 milliseconds later, main again invokes setValue() , this time passing in true (lines 7779). After another 200 milliseconds pass, the main thread calls pulseValue() to notify both kinds of transition waiters (lines 8183). Finally, main interrupts both of the threads that were started to get them to die (lines 8687).
The startTrueWaiter() method (lines 229) creates a new thread that continually waits on waitForFalseToTrueTransition() (line 14). This new thread is called threadA . Each time through the infinite while loop, threadA prints information about the state of the instance of TransitionDetector (lines 1117).
The startFalseWaiter() method (lines 3158) works much like startTrueWaiter() but instead waits on waitForTrueToFalseTransition() (line 43).
Listing 17.10 shows the output produced when TransitionDetectorMain is run. Due to the indeterminate nature of the thread scheduler, your output might vary slightly.
Listing 17.10  Possible Output from TransitionDetectorMain
1: threadA: about to wait for false-to-true transition, td=false
2: threadB: about to wait for true-to-false transition, td=false
3: main: td=false, about to set to ˜false
4: main: td=false, about to set to ˜true
5: threadA: just noticed for false-to-true transition, td=true
6: threadA: about to wait for false-to-true transition, td=true
7: main: td=true, about to pulse value
8: threadB: just noticed for true-to-false transition, td=true
9: threadB: about to wait for true-to-false transition, td=true
10: threadA: just noticed for false-to-true transition, td=true
11: threadA: about to wait for false-to-true transition, td=true
Both threadA and threadB start up and wait for their desired transitions (lines 12). Note that it does not matter what the current setting of value isboth threads are waiting for value to change . The main thread sets value to false , but this has no effect because value was already false (line 3). Next, main sets value to true (line 4), which causes threadA to return from waitForFalseToTrueTransition() (line 5) and to loop again (line 6). After another 200-millisecond delay, main invokes pulseValue() (line 7). This causes both threadA and threadB to return and report that they noticed the transitions that they were waiting for (lines 811). Notice that value is true before and after pulseValue() is invoked.

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