Chapter 8: Inter-thread Communication

Chapter 8 - Inter-thread Communication

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

CubbyHole Example
The class CubbyHole (see Listing 8.9) simulates a cubbyhole. A cubbyhole is a slot that can have only one item in it at a time. One thread puts an item into the slot and another thread takes it out. If a thread tries to put an item into a cubbyhole that is already occupied, the thread blocks until the slot is available. If a thread tries to remove an item from an empty cubbyhole, the thread blocks until an item is added. In this example, the slot is a reference to an object. This technique allows objects to be handed off from one thread to another in a thread-safe manner.
Listing 8.9  CubbyHole.javaObject Passing from One Thread to Another
1: public class CubbyHole extends Object {
2:     private Object slot;
3:
4:     public CubbyHole() {
5:         slot = null; // null indicates empty
6:     }
7:
8:     public synchronized void putIn(Object obj)
9:                         throws InterruptedException {
10:
11:         print(in putIn() - entering);
12:
13:         while (slot != null) {
14:             print(in putIn() - occupied, about to wait());
15:             wait(); // wait while slot is occupied
16:             print(in putIn() - notified, back from wait());
17:         }
18:
19:         slot = obj;  // put object into slot
20:         print(in putIn() - filled slot, about to notifyAll());
21:         notifyAll(); // signal that slot has been filled
22:
23:         print(in putIn() - leaving);
24:     }
25:
26:     public synchronized Object takeOut()
27:                         throws InterruptedException {
28:
29:         print(in takeOut() - entering);
30:
31:         while (slot == null) {
32:             print(in takeOut() - empty, about to wait());
33:             wait(); // wait while slot is empty
34:             print(in takeOut() - notified, back from wait());
35:         }
36:
37:         Object obj = slot;
38:         slot = null; // mark slot as empty
39:         print(
40:             in takeOut() - emptied slot, about to notifyAll());
41:         notifyAll(); // signal that slot is empty
42:
43:         print(in takeOut() - leaving);
44:         return obj;
45:     }
46:
47:     private static void print(String msg) {
48:         String name = Thread.currentThread().getName();
49:         System.out.println(name + : + msg);
50:     }
51: }
CubbyHole has a private member variable slot (line 2) that is used to hold a reference to the object that is being passed between the threads. In the constructor, slot is set to null to indicate that it is empty (line 5).
The putIn() method (lines 824) is synchronized (line 8) and declares that it might throw an InterruptedException (line 9). A while loop (lines 1317) is used to ensure that the thread that calls putIn() will not proceed until slot is empty. If slot is occupied, the calling thread invokes wait() on this (line 15) and releases the object-level lock it acquired just before entering putIn() . When slot is finally null , the thread proceeds to copy the passed parameter obj into slot (line 19) and invokes notifyAll() (line 21) to signal any and all waiting threads that data has become available.
The takeOut() method  (lines 2645) is synchronized (line 26) and declares that it might throw an InterruptedException (line 27). A while loop (lines 3135) is used to ensure that the thread will not proceed until slot is occupied. If slot is currently empty, the thread sleeps, waiting for notification that something has changed (line 33). When slot is finally filled, the thread proceeds to copy the reference into obj (line 37), sets slot to null to indicate that it is again empty (line 38), and invokes notifyAll() (line 41) to signal any and all waiting threads that something has changed.
CubbyHoleMain (see Listing 8.10) creates an instance of CubbyHole and starts two threads to interact with it.
Listing 8.10  CubbyHoleMain.javaUsed to Demonstrate CubbyHole
1: public class CubbyHoleMain extends Object {
2:     private static void print(String msg) {
3:         String name = Thread.currentThread().getName();
4:         System.out.println(name + : + msg);
5:     }
6:
7:     public static void main(String[] args) {
8:         final CubbyHole ch = new CubbyHole();
9:
10:         Runnable runA = new Runnable() {
11:                 public void run() {
12:                     try {
13:                         String str;
14:                         Thread. sleep(500);
15:
16:                         str = multithreaded;
17:                         ch. putIn (str);
18:                         print(in run() - just put in: ˜ +
19:                                 str + ˜);
20:
21:                         str = programming;
22:                         ch. putIn (str);
23:                         print(in run() - just put in: ˜ +
24:                                 str + ˜);
25:
26:                         str = with Java;
27:                         ch. putIn (str);
28:                         print(in run() - just put in: ˜ +
29:                                 str + ˜);
30:                     } catch (InterruptedException x) {
31:                         x.printStackTrace();
32:                     }
33:                 }
34:             };
35:
36:         Runnable runB = new Runnable() {
37:                 public void run() {
38:                     try {
39:                         Object obj;
40:
41:                         obj = ch. takeOut ();
42:                         print(in run() - just took out: ˜ +
43:                                 obj + ˜);
44:
45:                         Thread. sleep(500);
46:
47:                         obj = ch. takeOut ();
48:                         print(in run() - just took out: ˜ +
49:                                 obj + ˜);
50:
51:                         obj = ch. takeOut ();
52:                         print(in run() - just took out: ˜ +
53:                                 obj + ˜);
54:                     } catch (InterruptedException x) {
55:                         x.printStackTrace();
56:                     }
57:                 }
58:             };
59:
60:         Thread threadA = new Thread(runA, threadA);
61:         threadA.start();
62:
63:         Thread threadB = new Thread(runB, threadB);
64:         threadB.start();
65:     }
66: }
In the main() method (lines 765), an instance of CubbyHole is constructed and assigned to ch (line 8). threadA initially sleeps for 0.5 seconds to give threadB a chance to get going (line 14). threadA then proceeds to invoke putIn() three times in a row. Three String objects are passed one at a time into putIn() : multithreaded , programming , and with Java (lines 17, 22, and 27). CubbyHole can hold only one item at a time, so the second and third calls to putIn() may block waiting for threadB to remove items.
When threadB is started, it immediately invokes takeOut() (line 41) and blocks waiting for threadA to add something. After removing the first item, threadB sleeps for 0.5 seconds (line 45) to give threadA time to put the second item into slot and to block waiting to put in the third item. threadB then proceeds to take out the second and third items (lines 47, 51).
Listing 8.11 shows the output from a particular run of CubbyHoleMain . Because of thread-scheduling issues, your output might differ a little.
Listing 8.11  Possible Output from CubbyHoleMain
1: threadB: in takeOut() - entering
2: threadB: in takeOut() - empty, about to wait()
3: threadA: in putIn() - entering
4: threadA: in putIn() - filled slot, about to notifyAll()
5: threadA: in putIn() - leaving
6: threadA: in run() - just put in: ˜multithreaded
7: threadA: in putIn() - entering
8: threadA: in putIn() - occupied, about to wait()
9: threadB: in takeOut() - notified, back from wait()
10: threadB: in takeOut() - emptied slot, about to notifyAll()
11: threadB: in takeOut() - leaving
12: threadB: in run() - just took out: ˜multithreaded
13: threadA: in putIn() - notified, back from wait()
14: threadA: in putIn() - filled slot, about to notifyAll()
15: threadA: in putIn() - leaving
16: threadA: in run() - just put in: ˜programming
17: threadA: in putIn() - entering
18: threadA: in putIn() - occupied, about to wait()
19: threadB: in takeOut() - entering
20: threadB: in takeOut() - emptied slot, about to notifyAll()
21: threadB: in takeOut() - leaving
22: threadB: in run() - just took out: ˜programming
23: threadB: in takeOut() - entering
24: threadB: in takeOut() - empty, about to wait()
25: threadA: in putIn() - notified, back from wait()
26: threadA: in putIn() - filled slot, about to notifyAll()
27: threadA: in putIn() - leaving
28: threadA: in run() - just put in: ˜ with Java
29: threadB: in takeOut() - notified, back from wait()
30: threadB: in takeOut() - emptied slot, about to notifyAll()
31: threadB: in takeOut() - leaving
32: threadB: in run() - just took out: ˜ with Java
Notice that sometimes a thread enters one of the methods and does not have to wait (lines 34), and other times it does have to wait (lines 12). This output shows both the case of a thread blocking waiting to put an item in (lines 78) and the case of a thread blocking waiting for an item to remove (lines 2324). Even with all this complexity, the three String objects are delivered safely through the CubbyHole in the exact order that they were added.
Figure 8.4 shows the timeline of events from this run of CubbyHoleMain . The diagram reflects the internal workings that occurred to produce the output in Listing 8.11. Notice that both threads invoke wait() and notifyAll() . Both threads signal and listen for signals. Notice that to add and remove three items required 10 lock-unlock cycles on this . This kind of complexity is required to safely ensure that there are not any race conditions, missed notifications, or early notifications.
Figure 8.4: Timeline of events from a particular run of CubbyHoleMain.
  Note The basic premise behind CubbyHole can be expanded into a First-In-First-Out (FIFO) queue structure. In Chapter 18, First-In-First-Out (FIFO) Queue, Ill show you a technique for creating a FIFO queue. A FIFO queue with a capacity to hold exactly one item can be very useful for signaling between threads because it nicely encapsulates the complexity of the wait/notify mechanism.

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