Chapter 12: Exception Callback

Chapter 14 - Waiting for the Full Timeout

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Accidentally Returning Early
In EarlyReturn , shown in Listing 14.1, one thread blocks waiting for the internal value to reach a specified minimum while a second thread changes the value. Every time the second thread changes the value, it notifies any waiting threads. The first thread is waiting (up to the timeout) for a particular value to be reached. In this example, the first thread will accidentally return before the value is reached and before the timeout occurs.
Listing 14.1  EarlyReturn.javaAn Example of an Accidental Early Return
1: public class EarlyReturn extends Object {
2:     private volatile int value;
3:
4:     public EarlyReturn(int initialValue) {
5:         value = initialValue;
6:     }
7:
8:     public synchronized void setValue(int newValue) {
9:         if (value != newValue) {
10:             value = newValue;
11:             notifyAll();
12:         }
13:     }
14:
15:     public synchronized boolean waitUntilAtLeast(
16:                 int minValue,
17:                 long msTimeout
18:            ) throws InterruptedException {
19:
20:         System.out.println(entering waitUntilAtLeast() - +
21:                 value= + value +
22:                 ,minValue= + minValue);
23:
24:         if (value < minValue) {
25:             wait(msTimeout);
26:         }
27:
28:         System.out.println(leaving waitUntilAtLeast() - +
29:                 value= + value +
30:                 ,minValue= + minValue);
31:
32:         // May have timed out, or may have met value,
33:         // calc return value.
34:         return (value >= minValue);
35:     }
36:
37:     public static void main(String[] args) {
38:         try {
39:             final EarlyReturn er = new EarlyReturn(0);
40:
41:             Runnable r = new Runnable() {
42:                     public void run() {
43:                         try {
44:                             Thread.sleep(1500);
45:                             er.setValue(2);
46:                             Thread.sleep(500);
47:                             er.setValue(3);
48:                             Thread.sleep(500);
49:                             er.setValue(4);
50:                         } catch (Exception x) {
51:                             x.printStackTrace();
52:                         }
53:                     }
54:                 };
55:
56:             Thread t = new Thread(r);
57:             t.start();
58:
59:             System.out.println(
60:                     about to: waitUntilAtLeast(5, 3000));
61:             long startTime = System.currentTimeMillis();
62:             boolean retVal = er.waitUntilAtLeast(5, 3000);
63:             long elapsedTime =
64:                     System.currentTimeMillis() - startTime;
65:
66:             System.out.println(after + elapsedTime +
67:                     ms, retVal= + retVal);
68:         } catch (InterruptedException ix) {
69:             ix.printStackTrace();
70:         }
71:     }
72: }
EarlyReturn holds the last value set in the private member variable value (line 2). The constructor (lines 46) simply sets value to some initial state. The setValue() method (lines 813) is synchronized so it blocks until it can acquire the lock on this (the EarlyReturn instance). After the lock is acquired , setValue() checks to see if newValue is really new (line 9) to avoid unnecessary notifications. If it is different, setValue() sets value (line 10) and then notifies any and all threads waiting on this object for a change (line 11).
The waitUntilAtLeast() method (lines 1535) is used by threads that want to blockup to a specified amount of timewaiting for value to increase to the minimum specified. Any number of threads can be blocked simultaneously inside waitUntilAtLeast() as notifyAll() is used inside setValue() to be sure that all waiters are notified of the change. If the minimum value has not yet been met (line 24), the thread waits for up to msTimeout milliseconds for it to increase (line 25). Regardless of whether the minimum was achieved or the wait timed out, the boolean returned is calculated based on the current setting of value . If value is at least minValue , true is returned. Otherwise, false is returned to indicate a timeout.
The main() method (lines 3771) is used to create a new thread to call setValue() , while the main thread continues to block on waitUntilAtLeast() . A new EarlyReturn object is created with an initial value of (line 39). It is final so that it can be accessed from the anonymous inner class. A new Runnable is declared (lines 4154) to alternately sleep and call setValue() with new values (lines 4449). Next, the Thread for this Runnable is created and started (lines 5657).
The main thread then takes note of the current time to be able to measure how long waitUntilAtLeast() blocks  (line 61). The waitUntilAtLeast() method is invoked with a minimum value to wait for of 5 , and a maximum time to wait of three seconds (line 60). The boolean returned from waitUntilAtLeast() is captured into retVal . The elapsed time that the thread was blocked inside waitUntilAtLeast() is calculated (lines 6364) and printed along with retVal (lines 6667).
When EarlyReturn is run, output much like the following is produced (your output should almost match except for slightly different times):
about to: waitUntilAtLeast(5, 3000)
entering waitUntilAtLeast() - value=0,minValue=5
leaving waitUntilAtLeast() - value=2,minValue=5
after 1370 ms, retVal=false
The problem with this is that waitUntilAtLeast() returns before the minimum value of 5 is reached and before the timeout of 3000 milliseconds has elapsed. This isnt what waitUntilAtLeast() was designed to do!
Whats going on here is that after the new thread sleeps for 1500 milliseconds, it invokes er.setValue(2) (line 45). Inside setValue() , the notifyAll() (line 11) causes any and all waiting threads to return from wait() . By the time this happens, the main thread has been waiting for a little more than one second (line 25). The main thread returns from wait() , prints the current values, and calculates the boolean to return. In this case, false is returned because it was waiting for a minimum of 5 and value was only set to 2 . What is needed here is a way to tell if wait() returned because the timeout was reached, or if it returned because it was notified. If it returned because it was notified, a check needs to be added to see if minValue has been met yet. If it hasnt been met, wait() should be invoked again. If it returned because it timed out, no more waiting should be done.

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