The WaitNotify Mechanism

Chapter 8 - Inter-thread Communication

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Using ThreadLocal and InheritableThreadLocal
Support for thread-specific variables has been added as of release 1.2 of the JDK. The value returned from the get() method ThreadLocal depends on which thread invokes the method. InheritableThreadLocal allows these values to be inherited from parent to child thread.
Ill just give you a quick, high-level overview on how the thread-specific variables are implemented. Figure 8.6 shows the class relationships for ThreadLocal and InheritableThreadLocal . ThreadLocal contains a reference to a WeakHashMap that holds key-value pairs. Weak references were introduced in JDK 1.2, and WeakHashMap takes advantage of them to automatically remove mappings for threads that have died and been de-referenced in all other places. This way, ThreadLocal does not keep track of values for threads that have long since died. In the WeakHashMap , the lookup key is the reference to the Thread and the value stored is a ThreadLocal.Entry object. ThreadLocal.Entry is an inner class to ThreadLocal and is used by ThreadLocal to store the thread-specific values.
Figure 8.6: Class relationships for ThreadLocal and InheritableThreadLocal.
InheritableThreadLocal is a subclass of ThreadLocal that provides a mechanism for the thread-specific variable to be inherited from parent thread to child thread. InheritableThreadLocal.Entry is a subclass of ThreadLocal.Entry and is also an inner class. Thread contains a private reference to an InheritableThreadLocal.Entry object and uses it to pass the thread-specific variable down from parent thread to child thread when a new thread is created.
ThreadLocal API
ThreadLocal has two public methods . The first one is get() :
public Object get()
get() is used to retrieve the thread-specific value. Internally, it looks up to see if the calling thread has a value stored. If is does, it returns that value. If not, it calls the protected method initialValue() to initialize a value for the calling thread, stores it in the internal WeakHashMap for future lookups, and returns the value to the caller. Typically, get() is the only method called on a ThreadLocal object.
However, on rare occasions, a thread can set its own value for future use by invoking the other public method of ThreadLocal directly:
public void set(Object value)
set() takes the value passed and stores it in the internal WeakHashMap for future lookups.
ThreadLocal is not abstract , but it generally needs to be subclassed to be useful. This protected method should be overridden in the subclass:
protected Object initialValue()
By default, initialValue() returns null , but in the subclass it can return a more meaningful value.
ThreadID
The class ThreadID (see Listing 8.18) is a subclass of ThreadLocal and creates a unique ID for every thread that invokes get() . If a thread comes back and invokes get() again, the same value is returned.
Listing 8.18  ThreadID.javaUsing ThreadLocal to Generate Unique Thread IDs
1: public class ThreadID extends ThreadLocal {
2:     private int nextID;
3:
4:     public ThreadID() {
5:         nextID = 10001;
6:     }
7:
8:     private synchronized Integer getNewID() {
9:         Integer id = new Integer(nextID);
10:         nextID++;
11:         return id;
12:     }
13:
14:     // override ThreadLocals version
15:    protected Object initialValue() {
16:         print(in initialValue());
17:         return getNewID();
18:     }
19:
20:     public int getThreadID() {
21:         // Call get() in ThreadLocal to get the calling
22:         // threads unique ID.
23:         Integer id = (Integer) get() ;
24:         return id.intValue();
25:     }
26:
27:     private static void print(String msg) {
28:         String name = Thread.currentThread().getName();
29:         System.out.println(name + : + msg);
30:     }
31: }
ThreadID is a subclass of ThreadLocal (line 1) and creates a unique ID for every thread that invokes the get() method. The next unique ID to be assigned is held in nextID (line 2) and is initialized to be 10001 (line 5). The getNewID() method (lines 812) is synchronized to ensure that only one thread is inside it at a time. The initialValue() method (lines 1518) overrides the superclasss method and calls getNewID() to generate a new, unique ID.
The only public method is getThreadID() (lines 2025). Inside, get() is invoked to look up the value for the calling thread (line 23). This action might indirectly cause initialValue() to be called if it is the first time that the calling thread has invoked get() .
The class ThreadIDMain (see Listing 8.19) starts up three threads to demonstrate how ThreadID works. Each thread calls getThreadID() twice to show that the first call for each thread generates a new ID and that the second call simply returns the same value generated during the first call.
Listing 8.19  ThreadIDMain.javaUsed to Demonstrate ThreadID
1: public class ThreadIDMain extends Object implements Runnable {
2:     private ThreadID var ;
3:
4:     public ThreadIDMain(ThreadID var) {
5:         this.var = var;
6:     }
7:
8:     public void run() {
9:         try {
10:             print(var.getThreadID()= + var.getThreadID() );
11:             Thread.sleep(2000);
12:             print(var.getThreadID()= + var.getThreadID() );
13:         } catch (InterruptedException x) {
14:             // ignore
15:         }
16:     }
17:
18:     private static void print(String msg) {
19:         String name = Thread.currentThread().getName();
20:         System.out.println(name + : + msg);
21:     }
22:
23:     public static void main(String[] args) {
24:         ThreadID tid = new ThreadID();
25:         ThreadIDMain shared = new ThreadIDMain( tid );
26:
27:         try {
28:             Thread threadA = new Thread(shared, threadA);
29:             threadA.start();
30:
31:             Thread.sleep(500);
32:
33:             Thread threadB = new Thread(shared, threadB);
34:             threadB.start();
35:
36:             Thread.sleep(500);
37:
38:             Thread threadC = new Thread(shared, threadC);
39:             threadC.start();
40:         } catch (InterruptedException x) {
41:             // ignore
42:         }
43:     }
44: }
In main() (lines 2343), one instance of ThreadID is constructed (line 24) and passed into the constructor for ThreadIDMain (line 25). The instance of ThreadIDMain is referred to by shared and implements the Runnable interface. The main thread proceeds to create three new threads ( threadA , threadB , and threadC ) that will all simultaneously run the one instance referenced by shared (lines 2839).
When each thread runs, the thread invokes the run() method (lines 816). Inside run() , getThreadID() is invoked and the result is printed (line 10). After sleeping for two seconds (line 11), getThreadID() is invoked again and the result is printed (line 12).
Listing 8.20 shows the output produced when ThreadIDMain is run. Your output should match.
Listing 8.20  Output from ThreadIDMain
1: threadA: in initialValue()
2: threadA: var.getThreadID()=10001
3: threadB: in initialValue()
4: threadB: var.getThreadID()=10002
5: threadC: in initialValue()
6: threadC: var.getThreadID()=10003
7: threadA: var.getThreadID()=10001
8: threadB: var.getThreadID()=10002
9: threadC: var.getThreadID()=10003
When threadA invokes getThreadID() , it indirectly causes initialValue() to be called (line 1). The unique ID for threadA is 10001 (line 2). When threadB and threadC invoke getThreadID() , they both get a new unique ID (lines 36). The second time that each of the threads invokes getThreadID() , a unique ID does not have to be generated and the same ID that was returned the first time is returned again (lines 79).
InheritableThreadLocal API
InheritableThreadLocal is a subclass of ThreadLocal and allows a thread-specific value to be inherited from the parent thread to the child thread. There are not any public methods on InheritableThreadLocal . It can be used directly as a special kind of ThreadLocal that passes its value from parent thread to child thread.
If you dont want to use the parent threads value directly, you can override
protected Object childValue(Object parentValue)
to produce a customized child value at the time that the child thread is created. By default, childValue() simply returns parentValue .
InheritableThreadID
The class InheritableThreadID (see Listing 8.21) demonstrates three different ways that thread-specific variables can behave regarding inheritance from parent thread to child thread. First, a ThreadLocal variable is used to demonstrate that the child thread will have a different thread-specific value than its parent thread does. Second, an InheritableThreadLocal is used without overriding childValue() to demonstrate that the child thread will have the exact same thread-specific value as its parent. Third, an InheritableThreadLocal is used with the childValue() method overridden to demonstrate that the childs value can be based on the parents value.
Listing 8.21  InheritableThreadID.javaDemonstration of InheritableThreadLocal
  1: public class InheritableThreadID extends Object {
  2:     public static final int UNIQUE  = 101;
  3:     public static final int INHERIT = 102;
  4:     public static final int SUFFIX  = 103;
  5:
  6:     private ThreadLocal threadLocal;
  7:     private int nextID;
  8:
  9:     public InheritableThreadID(int type) {
10:         nextID = 201;
11:
12:         switch (type) {
13:             case UNIQUE:
14:                 threadLocal = new ThreadLocal() {
15:                         // override from ThreadLocal
16:                         protected Object initialValue() {
17:                             print(in initialValue());
18:                             return getNewID();
19:                         }
20:                     };
21:                 break;
22:
23:             case INHERIT:
24:                 threadLocal = new InheritableThreadLocal() {
25:                         // override from ThreadLocal
26:                         protected Object initialValue() {
27:                             print(in initialValue());
28:                             return getNewID();
29:                         }
30:                     };
31:                 break;
32:
33:             case SUFFIX:
34:                 threadLocal = new InheritableThreadLocal() {
35:                         // override from ThreadLocal
36:                         protected Object initialValue() {
37:                             print(in initialValue());
38:                             return getNewID();
39:                         }
40:
41:                         // override from InheritableThreadLocal
42:                         protected Object childValue (
43:                                     Object parentValue
44:                                ) {
45:
46:                             print(in childValue() - +
47:                                 parentValue= + parentValue);
48:
49:                             return parentValue + -CH;
50:                         }
51:                     };
52:                 break;
53:             default:
54:                 break;
55:         }
56:     }
57:
58:     private synchronized String getNewID() {
59:         String id = ID + nextID;
60:         nextID++;
61:         return id;
62:     }
63:
64:     public String getID() {
65:         return (String) threadLocal.get() ;
66:     }
67:
68:     public static void print(String msg) {
69:         String name = Thread.currentThread().getName();
70:         System.out.println(name + : + msg);
71:     }
72:
73:     public static Runnable createTarget(InheritableThreadID id) {
74:         final InheritableThreadID var = id;
75:
76:         Runnable parentRun = new Runnable() {
77:             public void run() {
78:                 print(var.getID()= + var.getID() );
79:                 print(var.getID()= + var.getID());
80:                 print(var.getID()= + var.getID());
81:
82:                 Runnable childRun = new Runnable() {
83:                         public void run() {
84:                             print(var.getID()= + var.getID() );
85:                             print(var.getID()= + var.getID());
86:                             print(var.getID()= + var.getID());
87:                         }
88:                     };
89:
90:                 Thread parentT = Thread.currentThread();
91:                 String parentName = parentT.getName();
92:                 print(creating a child thread of +
93:                     parentName);
94:
95:                 Thread childT = new Thread(childRun,
96:                         parentName + -child);
97:                 childT.start();
98:             }
99:         };
100:
101:         return parentRun;
102:     }
103:
104:     public static void main(String[] args) {
105:         try {
106:             System.out.println(======= ThreadLocal =======);
107:             InheritableThreadID varA =
108:                 new InheritableThreadID( UNIQUE );
109:
110:             Runnable targetA = createTarget(varA);
111:             Thread threadA = new Thread(targetA, threadA);
112:             threadA.start();
113:
114:             Thread.sleep(2500);
115:             System.out.println(\n======= +
116:                 InheritableThreadLocal =======);
117:
118:             InheritableThreadID varB =
119:                 new InheritableThreadID( INHERIT );
120:
121:             Runnable targetB = createTarget(varB);
122:             Thread threadB = new Thread(targetB, threadB);
123:             threadB.start();
124:
125:             Thread.sleep(2500);
126:             System.out.println(\n======= +
127:                 InheritableThreadLocal - custom childValue() +
128:                 =======);
129:
130:             InheritableThreadID varC =
131:                 new InheritableThreadID( SUFFIX );
132:
133:             Runnable targetC = createTarget(varC);
134:             Thread threadC = new Thread(targetC, threadC);
135:             threadC.start();
136:         } catch (InterruptedException x) {
137:             // ignore
138:         }
139:
140:     }
141: }
In the constructor for InheritableThreadID (lines 956), one of three different ThreadLocal instances is created. If type is UNIQUE (line 13), a plain ThreadLocal is used with its initialValue() method overridden to call getNewID() (lines 1619). If type is INHERIT , an InheritableThreadLocal is used with its initialValue() method overridden to call getNewID() (lines 2629). If type is SUFFIX , an InheritableThreadLocal is used with its initialValue() method overridden to call getNewID() (lines 3639) and its childValue() method overridden to return the parents value with -CH appended to it (lines 4250).
Every time the synchronized method getNewID() is called, it generates a new String -based ID and returns it (lines 5862).
When the getID() method (lines 6466) is called, it invokes the get() method on whichever one of the three types of ThreadLocal variables was created in the constructor.
The static method createTarget() (lines 73102) is used by main() to create a Runnable . When run() is invoked, the getID() method for the particular instance of InheritableThreadID is invoked three times (lines 7880). Then this parent thread creates a new child thread (lines 9597). The child thread is then started and invokes getID() three more times to see what value is returned to the child thread (lines 8486).
In the main() method (lines 104140), three different instances of InheritableThreadID are constructed and three different threads work with them.
First, varA refers to an InheritableThreadID that simply creates a unique ID for every threadregardless of parent-child relationships (lines 107108). A Runnable is created to use varA (line 110), and threadA is started to run it (lines 111112).
Second, varB refers to an InheritableThreadID that allows the parent thread to pass its ID unmodified to the child threadthe child inherits the parents value (lines 118119). A Runnable is created to use varB (line 121), and threadB is started to run it (lines 122123).
Third, varC refers to an InheritableThreadID that intercepts the passing of the value from the parent to child and adds a suffix to the childs value (lines 130131). A Runnable is created to use varC (line 133), and threadC is started to run it (lines 134135).
Listing 8.22 shows the output produced when InheritableThreadID is run. Your output should match.
Listing 8.22  Output Produced When InheritableThreadID Is Run
1: ======= ThreadLocal =======
2: threadA: in initialValue()
3: threadA: var.getID()=ID201
4: threadA: var.getID()=ID201
5: threadA: var.getID()=ID201
6: threadA: creating a child thread of threadA
7: threadA-child: in initialValue()
8: threadA-child: var.getID()=ID202
9: threadA-child: var.getID()=ID202
10: threadA-child: var.getID()=ID202
11:
12: ======= InheritableThreadLocal =======
13: threadB: in initialValue()
14: threadB: var.getID()=ID201
15: threadB: var.getID()=ID201
16: threadB: var.getID()=ID201
17: threadB: creating a child thread of threadB
18: threadB-child: var.getID()=ID201
19: threadB-child: var.getID()=ID201
20: threadB-child: var.getID()=ID201
21:
22: ======= InheritableThreadLocal - custom childValue() =======
23: threadC: in initialValue()
24: threadC: var.getID()=ID201
25: threadC: var.getID()=ID201
26: threadC: var.getID()=ID201
27: threadC: creating a child thread of threadC
28: threadC: in childValue() - parentValue=ID201
29: threadC-child: var.getID()=ID201-CH
30: threadC-child: var.getID()=ID201-CH
31: threadC-child: var.getID()=ID201-CH
Notice that when UNIQUE is used, the parent and child have completely different ID values: ID201 and ID202 (lines 110). When INHERIT is used, the parent and child have exactly the same ID values: ID201 (lines 1220). When SUFFIX is used, the childs value is the same as the parents with -CH added to the end: ID201 and ID201-CH (lines 2231).

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