Java线程间通讯
考虑经典的排队问题,其中一个线程正在生产一些数据,另一个是消费它。为了使问题更有趣,假设生产者必须等待,直到它会产生更多的数据消费完毕之前。
在一个轮询系统,消费者会浪费大量的CPU周期,而它等待着生产者生产。一旦生产结束了,就开始轮询,浪费更多的CPU周期等待消费者完成,依此类推。显然,这种情况是不希望的。
为了避免轮询,Java包括通过下面的方法优雅的进程间通信机制:
-
wait( ): 这个方法告诉调用线程放弃监视器和进入睡眠状态,直到其他线程进入同一监视器和调用notify()。
-
notify( ): 这种方法唤醒第一个线程调用wait()在同一个对象上。
-
notifyAll( ): 这种方法唤醒所有调用wait()的同一个对象上的线程。最高优先级的线程将首先运行。
这些方法被实现为final的方法在Object,因此所有的类都有它们。这三种方法都只能从一个同步的上下文中被调用。
这些方法的对象中声明。各种形式的wait( ) 存在,使可以指定一段时间等待。
例子:
下面的示例程序包括四个类:Q,想同步队列,Producer,也就是生产队列的条目线程对象;Consumer,即消耗队列的条目线程对象和PC,tiny类创建一个Q,生产者和消费者。
写这个程序在Java中正确的方法是使用wait()和notify()方法,以在两个方向的信号,如下所示:
class Q { int n; boolean valueSet = false; synchronized int get() { if(!valueSet) try { wait(); } catch(InterruptedException e) { System.out.println("InterruptedException caught"); } System.out.println("Got: " + n); valueSet = false; notify(); return n; } synchronized void put(int n) { if(valueSet) try { wait(); } catch(InterruptedException e) { System.out.println("InterruptedException caught"); } this.n = n; valueSet = true; System.out.println("Put: " + n); notify(); } } class Producer implements Runnable { Q q; Producer(Q q) { this.q = q; new Thread(this, "Producer").start(); } public void run() { int i = 0; while(true) { q.put(i++); } } } class Consumer implements Runnable { Q q; Consumer(Q q) { this.q = q; new Thread(this, "Consumer").start(); } public void run() { while(true) { q.get(); } } } public class PCFixed { public static void main(String args[]) { Q q = new Q(); new Producer(q); new Consumer(q); System.out.println("Press Control-C to stop."); } }
内部的get(),wait()调用。这将导致其执行暂停,直到生产者通知有些数据已准备就绪。
当发生这种情况,执行里面的get( ) 恢复。该数据已获得后,get() 调用notify()。这告诉生产者,这都是可以的把更多的数据在队列中。
内部的put(),wait()暂停执行,直到消费者已经从队列中删除的项目。当恢复执行,数据的下一个项目被放入队列中,notify()被调用。这就告诉消费者,它现在应该将其删除。
下面是这个程序,它显示了干净的同步行为的一些输出:
Put: 1 Got: 1 Put: 2 Got: 2 Put: 3 Got: 3 Put: 4 Got: 4 Put: 5 Got: 5