Guard Block,是一种常见的多个线程通信的代码形式。例如下面的代码,就可以称作Guard Block。这里有一个空循环,joy初始值初始值为false,程序将进入while循环中,直到joy的值变为false为止。这里的joy就是Guard Block中Guard的含义,像是一个守卫一样。
publicvoidguardedJoy() {
// Simple loop guard. Wastes// processor time. Don't do this!while(!joy) {}
System.out.println("Joy has been achieved!");
}
但是我们并不应该像上面的方式一样书写代码,以为while循环会一直执行,这将消耗CPU的能力。因此,我们下面要说面一种Java语言的多线程间的通信机制。不知道大家还是否记得,我们在前面介绍interrupt的时候说过,interrupt本身也是一种通信机制,是让设计成耗时任务可以抛出InterruptException的方法中断,停止执行,代码走异常处理。今天这要说的这两个(或者说三个)方法,是专门设计成线程间通信的。
wait
首先说wait方法是Object类的方法,并且声明成final的,不能被子类重写。因此我们创建一个类,并在其中调用wait方法时,实际上调用的就是Object中的wait方法。wait方法是可以让线程挂起,暂停执行的。我们看一下用wait方法更改上面的代码。
public synchronized voidguardedJoy() {
// This guard only loops once for each special event, which may not// be the event we're waiting for.while(!joy) {
try {
wait();
} catch (InterruptedException e) {}
}
System.out.println("Joy and efficiency have been achieved!");
}
与其他耗时方法一样,wait方法也是抛出了InterruptException。需要我们捕获处理。wait方法会使得线程一直阻塞在那里,知道某种事件发生才会唤醒(后面马上会提到)。
除此之外,新方法变成了synchronized修饰的同步方法。这是由于wait方法执行之前,需要首先获取对象的内在锁。否则将会抛出一个错误,而通过加锁的方式,我们获取了这个内在锁。当wait执行了之后,线程挂起。这个时候,线程一直停在wait方法处,同时,wait方法释放了刚刚获取的内在锁。
notify,notifyAll
前面说到,wait方法挂起了当前线程后释放了内在锁。当另一个线程想要唤醒那个线程的时候,调用notify或者notifyAll,也需要获得同一把锁。
publicsynchronizednotifyJoy() {
joy = true;
notifyAll();
}
当notify或者notifyAll所在方法执行完毕后,会释放该锁,而此时wait方法再次获得该锁,进而暂停的线程可以继续执行。
notify和notifyAll的区别在于,由于wait方法挂起了线程后就释放掉了内在锁,所以,多个线程可以多次调用guardedJoy方法,也就是多个线程可以同时挂起,这是,如果调用notifyAll,那么所有挂起的线程都会醒来,而notify只能唤醒一个,并且不能确定是哪一个被唤醒。