Java线程间通信
下一节
上述例题导致另一个线程无条件地阻止对一个方法的异步访问。 隐式管理进程在Java对象中的应用功能很强,但通过进程间通信可以到达更微妙的区域。 这在Java中特别简单。
如上文所述,多线程将任务划分成离散且逻辑的单元,而不是事件循环。 线程还具有远离轮询的第二个优点。 轮询通常通过循环重复监视条件来实现。 条件成立后,必须采取适当行动。 这浪费了CPU时间。 例如,考虑一下经典序列问题。 当一个线程生成数据,而另一个程序正在消耗数据时。 假设为了使问题更有趣,数据生成器必须等待消费者完成工作才能生成新数据。 轮询系统会导致消费者在等待生产者生成数据时浪费很多CPU周期。 生产者完成工作后,开始轮询,浪费更多的CPU时间等待消费者的工作完成。 很明显,这种情况不受欢迎。
为了避免轮询,Java包含由wait (、notify )和notifyAll )方法实现的进程间通信机制。 这些方法在对象中用final方法实现,因此包含在所有类中。 这三个方法仅在同步方法中调用。 尽管这些方法从计算机科学视野的方向看具有概念的高度先进性,但实际使用起来很简单。
wit ) )指示调用的线程放弃调度并进入休眠,直到其他线程进入同一线程,并调用通告)。
通告) )恢复调用同一对象中第一个等待)的线程。
恢复调用通告所有(是同一对象中的等待)的所有线程。 优先级最高的线程首先运行。
这些方法在Object中声明如下:
finalvoidwait (throwsinterruptedexception )
final void notify (
final void通知全部()
wit ) )存在的另一种形式可以定义等待时间。
下面的示例程序错误地执行了简单的生产者/消费者问题。 它由四个班组成。 q、获取同步序列的方法; Producer,用于生成排队的线程对象; Consumer,消费序列的线程对象; 然后创建PC、各个q、Producer和Consumer的小类。
//anincorrectimplementationofaproducerandconsumer。
类q {
int n;
同步输入获取(
system.out.println(got: ) n;
返回n;
}
同步化语音(intn ) {
this.n=n;
system.out.println(put: ) n;
}
}
classproducerimplementsrunnable {
q;
产品开发人员(QQ ) {
this.q=q;
newthread(this,' Producer ' ).start );
}
公共void run (}
int i=0;
while (真)。
q.put(I );
}
}
}
classconsumerimplementsrunnable {
q;
客户(QQ ) {
this.q=q;
newthread(this,' Consumer ' ).start );
}
公共void run (}
while (真)。
q.get (;
}
}
}
class PC {
publicstaticvoidmain (string args [ ] ) {
问Q q=new Q (;
新生产程序(q;
新消费者(q;
system.out.println (press control-CTO stop.';
}
}
尽管q级put (和get )方法同步,但既没有阻止生产者超越消费者,也没有阻止消费者两次消费同一序列。 这样可以得到以下错误输出。 输出取决于处理器的速度和加载的任务。
Put: 1
Got: 1
Got: 1
Got: 1
Got: 1
Got: 1
Put: 2
Put: 3
Put: 4
Put: 5
Put:
6Put: 7
Got: 7
生产者生成1后,消费者依次获得同样的1五次。生产者在继续生成2到7,消费者没有机会获得它们。
用Java正确的编写该程序是用wait( )和notify( )来对两个方向进行标志,如下所示:
// A correct implementation of a producer and consumer.
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();
}
}
}
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( )被调用。这使执行挂起直到Producer 告知数据已经预备好。这时,内部get( ) 被恢复执行。获取数据后,get( )调用notify( )。这告诉Producer可以向序列中输入更多数据。在put( )内,wait( )挂起执行直到Consumer取走了序列中的项目。当执行再继续,下一个数据项目被放入序列,notify( )被调用,这通知Consumer它应该移走该数据。
下面是该程序的输出,它清楚的显示了同步行为:
Put: 1
Got: 1
Put: 2
Got: 2
Put: 3
Got: 3
Put: 4
Got: 4
Put: 5
Got: 5
下一节>