【Java并发编程】控制块(保护块)

2019-07-14 12:25发布

Guard Block

Guard Block,是一种常见的多个线程通信的代码形式。例如下面的代码,就可以称作Guard Block。这里有一个空循环,joy初始值初始值为false,程序将进入while循环中,直到joy的值变为false为止。这里的joy就是Guard Block中Guard的含义,像是一个守卫一样。 public void guardedJoy() { // 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 void guardedJoy() { // 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,也需要获得同一把锁。 public synchronized notifyJoy() { joy = true; notifyAll(); } 当notify或者notifyAll所在方法执行完毕后,会释放该锁,而此时wait方法再次获得该锁,进而暂停的线程可以继续执行。 notify和notifyAll的区别在于,由于wait方法挂起了线程后就释放掉了内在锁,所以,多个线程可以多次调用guardedJoy方法,也就是多个线程可以同时挂起,这是,如果调用notifyAll,那么所有挂起的线程都会醒来,而notify只能唤醒一个,并且不能确定是哪一个被唤醒。