我们可以使用管道进行线程间通信,Java IO中提供了PipedWriter类和PipedReader类让线程间通信,《Java编程思想》在21.5.5节提供了例子。但是,Java提供了更健壮和容易的方式,即阻塞队列BlockingQueue来解决所有线程间协作的问题。所以,我们可以用BlockingQueue来实现线程间通信,原理很简单,一个线程向队列中添加元素,而另一个线程从队列中获取元素。
贴代码:(修改21.5.5的PipedIO.java,参考了21.5.4的吐司BlockingQueue程序ToastOMatic.java)
import java.util.concurrent.*;
import java.util.*;
class MessageQueue extends LinkedBlockingQueue<Character> {}
class Sender implements Runnable {
private Random rand = new Random(47);
private MessageQueue messageQueue;
public Sender(MessageQueue mq) { messageQueue = mq; }
public void run() {
try {
while(!Thread.interrupted())
for (char c = 'A'; c <= 'z'; c++) {
messageQueue.put(c);
TimeUnit.MILLISECONDS.sleep(rand.nextInt(500));
}
} catch(InterruptedException e) {
System.out.println(e + " Sender sleep interrupted");
}
}
}
class Receiver implements Runnable {
private MessageQueue messageQueue;
public Receiver(MessageQueue mq) { messageQueue = mq; }
public void run() {
try {
while(!Thread.interrupted()) {
System.out.print("Read: " + messageQueue.take() + ", ");
}
} catch(InterruptedException e) {
System.out.println(e + " Receiver read exception");
}
}
}
public class BlockingQueueIO {
public static void main(String[] args) throws Exception {
MessageQueue messageQueue = new MessageQueue();
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Sender(messageQueue));
exec.execute(new Receiver(messageQueue));
TimeUnit.SECONDS.sleep(4);
exec.shutdownNow();
}
}
编译并运行之后结果:
Read: A, Read: B, Read: C, Read: D, Read: E, Read: F, Read: G, Read: H, Read: I, Read: J, Read: K, Read: L, Read: M, Read: N, Read: O, Read: P, Read: Q, java.lang.InterruptedException Receiver read exception
java.lang.InterruptedException: sleep interrupted Sender sleep interrupted
我们可以看到,Sender和Receiver两个线程共用一个BlockingQueue,更多的线程都能使用同一个阻塞队列进行通信。每个线程都只和这个BlockingQueue通信,线程之间实际上没有直接的通信。而使用管道,其实两个线程是耦合在一起的。