The Need for Inter-thread Signaling

Chapter 8 - Inter-thread Communication

Java Thread Programming
Paul Hyde
  Copyright 1999 Sams Publishing

Streaming Data Between Threads Using Pipes
The java.io package provides many classes for writing and reading data to and from streams. Most of the time, the data is written to or read from a file or network connection. Instead of streaming data to a file, a thread can stream it through a pipe to another thread. The first thread writes to the pipe, and the second thread reads from the pipe. A pipe is neither a file nor a network connection, but a structure in memory that holds the data that is written until it is read. Usually, a pipe has a fixed capacity. When the pipe is filled to this capacity, attempts to write more data will block waiting until some data is drained (read) from the pipe by another thread. Similarly, when a pipe is empty, attempts to read data from the pipe will block waiting until another thread writes some data into it.
There are four pipe- related classes in the java.io package that can be used to stream data between threads: PipedInputStream, PipedOutputStream, PipedReader, and PipedWriter. A PipedInputStream and a PipedOutputStream are hooked together to transfer bytes between threads. A PipedReader and a PipedWriter are hooked together to transfer character data between threads. Figure 8.5 shows the class diagram for these classes. The PipedOutputStream object keeps a reference to the PipedInputStream object it is connected to. Similarly, the PipedWriter object keeps a reference to the PipedReader object it is connected to.
A pipe made up of a PipedInputStream and a PipedOutputStream has a capacity to hold 1024 bytes. This means that the thread doing the writing can be up to 1024 bytes ahead of the thread doing the reading. This buffering makes the transfer of data more efficient than a single-byte handoff would be. A pipe made up of a PipedReader and a PipedWriter has a capacity to hold 1024 characters . Again, this buffering allows the thread doing the writing to work a little bit ahead of the thread doing the reading. I discovered the size of the pipes (1024 bytes and 1024 characters) by examining the source code from Sun Microsystems. The API documentation gives no information or guarantees regarding the internal pipe size. Therefore, you should not depend on 1024 being the universal size.
Figure 8.5: Class diagram for the pipe-related classes in java.io.
PipedInputStream and PipedOutputStream each represent an end of the pipe and need to be connected to each other before data can be sent. Both PipedInputStream and PipedOutputStream have a constructor that takes a reference to the other. It doesnt matter which is constructed first. You can write either
PipedInputStream pipeIn = new PipedInputStream();
PipedOutputStream pipeOut = new PipedOutputStream( pipeIn );
or
PipedOutputStream pipeOut = new PipedOutputStream();
PipedInputStream pipeIn = new PipedInputStream( pipeOut );
Additionally, both ends can be created with their zero-argument constructors and connected together with connect() . You can write either
PipedInputStream pipeIn = new PipedInputStream();
PipedOutputStream pipeOut = new PipedOutputStream();
pipeIn. connect( pipeOut );
or
PipedInputStream pipeIn = new PipedInputStream();
PipedOutputStream pipeOut = new PipedOutputStream();
pipeOut .connect( pipeIn );
If the ends of the pipe are not yet connected to each other, any attempt to read or write will cause an IOException to be thrown. Because of this, its generally a good idea to connect the ends right away by using the constructor. PipedReader and PipedWriter connect to each other in the same ways that PipedInputStream and PipedOutputStream do, so the same rules and guidelines apply.
PipedBytes
The PipedBytes class  (see Listing 8.14) shows how data can be sent through a pipe from one thread to another. Integers are written to a PipedOutputStream by one thread and are read from a PipedInputStream by another thread.
Listing 8.14  PipedBytes.javaSending Data Between Threads Using PipedInputStream and PipedOutputStream
1: import java.io.*;
2:
3: public class PipedBytes extends Object {
4:     public static void writeStuff(OutputStream rawOut) {
5:         try {
6:             DataOutputStream out = new DataOutputStream(
7:                     new BufferedOutputStream(rawOut));
8:    
9:             int[] data = { 82, 105, 99, 104, 97, 114, 100, 32,
10:                            72, 121, 100, 101 };
11:
12:             for (int i = 0; i < data.length; i++) {
13:                 out.writeInt(data[i]);
14:             }
15:
16:             out.flush();
17:             out.close();
18:         } catch (IOException x) {
19:             x.printStackTrace();
20:         }
21:     }
22:
23:     public static void readStuff(InputStream rawIn) {
24:         try {
25:             DataInputStream in = new DataInputStream(
26:                     new BufferedInputStream(rawIn));
27:
28:             boolean eof = false;
29:             while (!eof) {
30:                 try {
31:                     int i = in.readInt();
32:                     System.out.println(just read: + i);
33:                 } catch (EOFException eofx) {
34:                     eof = true;
35:                 }
36:             }
37:
38:             System.out.println(Read all data from the pipe);
39:         } catch (IOException x) {
40:             x.printStackTrace();
41:         }
42:     }
43:
44:     public static void main(String[] args) {
45:         try {
46:             final PipedOutputStream out =
47:                     new PipedOutputStream();
48:
49:             final PipedInputStream in =
50:                     new PipedInputStream(out);
51:
52:             Runnable runA = new Runnable() {
53:                     public void run() {
54:                         writeStuff( out );
55:                     }
56:                 };
57:
58:             Thread threadA = new Thread(runA, threadA);
59:             threadA.start();
60:
61:             Runnable runB = new Runnable() {
62:                     public void run() {
63:                         readStuff( in );
64:                     }
65:                 };
66:
67:             Thread threadB = new Thread(runB, threadB);
68:             threadB.start();
69:         } catch (IOException x) {
70:             x.printStackTrace();
71:         }
72:     }
73: }
In the main() method  (lines 4472), a data pipe is created and two threads are started to transfer data through it. First, the PipedOutputStream is constructed (lines 4647). Next, the associated PipedInputStream is created by passing a reference to the PipedOutputStream that makes up the other end of the pipe (lines 4950). threadA is started and executes the writeStuff() method, passing in a reference to the PipedOutputStream (line 54). threadB is started and executes the readStuff() method, passing in a reference to the PipedInputStream (line 63). threadA is writing data into one end of the pipe while threadB is simultaneously reading data from the other end of the pipe.
The writeStuff() method (lines 421) only expects an OutputStream to be passed to it and makes no special considerations for the fact that it might just happen to be a PipedOutputStream (line 4). When threadA invokes writeStuff() , it wraps the OutputStream in a DataOuptutStream to be able to use the writeInt() method to put the integers into the pipe (lines 67). The integer data to be sent is stored into the int[] referred to by data (lines 910). Each of the integers in the array is written to the DataOutputStream (lines 1214). Before returning, the stream is flushed and closed (lines 1617).
Like writeStuff() , the readStuff() method (lines 2342) only expects an InputStream to be passed to it and makes no special considerations for the fact that it might happen to be a PipedInputStream (line 23). This raw InputStream is wrapped in a DataInputStream to facilitate the reading of integers (lines 2526). As long as the end-of-file is not detected , threadB continues to read integers from the stream (lines 2836).
Listing 8.15 shows the output produced when PipedBytes is run. Your output should match. Notice that all the integers are read by threadB in exactly the same order as they are written by threadA .
Listing 8.15  Output from PipedBytes
1: just read: 82
2: just read: 105
3: just read: 99
4: just read: 104
5: just read: 97
6: just read: 114
7: just read: 100
8: just read: 32
9: just read: 72
10: just read: 121
11: just read: 100
12: just read: 101
13: Read all data from the pipe
PipedCharacters
The PipedCharacters class (see Listing 8.16) shows how character-based data can be sent through a pipe from one thread to another. Strings are written to a PipedWriter by one thread and are read from a PipedReader by another thread.
Listing 8.16  PipedCharacters.javaSending Characters Between Threads Using PipedReader and PipedWriter
1: import java.io.*;
2:
3: public class PipedCharacters extends Object {
4:     public static void writeStuff(Writer rawOut) {
5:         try {
6:             BufferedWriter out = new BufferedWriter(rawOut);
7:    
8:             String[][] line = {
9:                     { Java, has, nice, features. },
10:                     { Pipes, are, interesting. },
11:                     { Threads, are, fun, in, Java. },
12:                     { Dont, you, think, so? }
13:                 };
14:
15:             for (int i = 0; i < line.length; i++) {
16:                 String[] word = line[i];
17:
18:                 for (int j = 0; j < word.length; j++) {
19:                     if (j > 0) {
20:                         // put a space between words
21:                         out.write();
22:                     }
23:
24:                     out.write(word[j]);
25:                 }
26:
27:                 // mark the end of a line
28:                 out.newLine();
29:             }
30:
31:             out.flush();
32:             out.close();
33:         } catch (IOException x) {
34:             x.printStackTrace();
35:         }
36:     }
37:
38:     public static void readStuff(Reader rawIn) {
39:         try {
40:             BufferedReader in = new BufferedReader(rawIn);
41:
42:             String line;
43:             while ((line = in.readLine()) != null) {
44:                 System.out.println(read line: + line);
45:             }
46:
47:             System.out.println(Read all data from the pipe);
48:         } catch (IOException x) {
49:             x.printStackTrace();
50:         }
51:     }
52:
53:     public static void main(String[] args) {
54:         try {
55:             final PipedWriter out = new PipedWriter();
56:
57:             final PipedReader in = new PipedReader(out);
58:
59:             Runnable runA = new Runnable() {
60:                     public void run() {
61:                         writeStuff( out );
62:                     }
63:                 };
64:
65:             Thread threadA = new Thread(runA, threadA);
66:             threadA.start();
67:    
68:             Runnable runB = new Runnable() {
69:                     public void run() {
70:                         readStuff( in );
71:                     }
72:                 };
73:    
74:             Thread threadB = new Thread(runB, threadB);
75:             threadB.start();
76:         } catch (IOException x) {
77:             x.printStackTrace();
78:         }
79:     }
80: }
In the main() method  (lines 5379), a character-based data pipe is created and two threads are started to transfer data through it. First, a PipedWriter is constructed (line 55). Next, the associated PipedReader is constructed by passing a reference to the PipedWriter (line 57). threadA is started and executes the writeStuff() method, passing in a reference to the PipedWriter (line 61). threadB is started and executes the readStuff() method, passing in a reference to the PipedReader (line 70). threadA is writing characters into one end of the pipe while threadB is simultaneously reading characters from the other end of the pipe.
The writeStuff() method (lines 436) only expects a Writer to be passed to it and makes no special considerations for the fact that it might be a PipedWriter (line 4). When threadA invokes writeStuff() , it wraps the Writer in a BufferedWriter to be able to use the newLine() method to mark the end of each line (line 6). The sentences to be sent are stored in the String[][] referred to by line (lines 813). The two-dimensional array stores the sentences in one dimension and the individual words that make up each sentence in the other dimension. Each line ( sentence ) is stepped through using the outer for loop (lines 1529). Each word in a line is stepped through using the inner for loop (lines 1825). After the last word of each line is written, the newLine() method is used to mark the end-of-line (line 28). Before returning, the BufferedWriter is flushed and closed (lines 3132).
The readStuff() method (lines 3851) only expects a Reader to be passed to it and makes no special considerations for the fact that it might happen to be a PipedReader (line 38). This raw Reader is wrapped in a BufferedReader to facilitate the reading of whole lines at a time (line 40). Each line is read from the BufferedReader until the end-of-file is detected by a null return from readLine() (lines 4245).
Listing 8.17 shows the output produced when PipedCharacters is run. Your output should match. Notice that all the lines (sentences) are read by threadB in exactly the same order that they are written by threadA .
Listing 8.17  Output from PipedCharacters
1: read line: Java has nice features.
2: read line: Pipes are interesting.
3: read line: Threads are fun in Java.
4: read line: Dont you think so?
5: Read all data from the pipe

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