wait()和notify()以及notifyAll()方法
wait()使当前线程进入等待队列,并释放该线程所拥有的锁
,(而sleep()方法挂起当前线程,并不释放该线程所拥有的锁),等待某个条件发生变化,再由其他线程唤醒(notify)。
wait()方法可以有一个参数,即等待的最长时间,若等待时长超过最长时间,该线程会从wait()中恢复执行。
notify()用来唤醒由wait()引起的被挂起的线程,当一个线程调用notify()时,在众多等待同一个锁的任务中只有一个会被唤醒。notifyAll()将唤醒所有等待当前线程所拥有的锁
的线程,但只有一个被唤醒的线程获得锁。
实际上,只能在同步代码块中调用wait(),nofity()和notifyAll()
,因为这些方法都操作锁。如果不在同步代码块中调用这些方法,程序能通过编译,但在运行的时候,会得到IllegalMonitorStateExceptiion异常。
这三个方法是基类Object的一部分
,而不是Thread的一部分。因为这些方法操作的锁也是所有对象的一部分。
当一个线程调用notifyAll()方法时,并不会立即释放锁
,只有在同步代码块语句执行完后,才释放锁。代码演示如下:
class NotifyDest{
public synchronized void f() {
System.out.println("f()");
notifyAll();//唤醒等待该锁的所有线程
System.out.println("exit");
}
public synchronized void g() {
try {
wait();//挂起当前线程,并释放锁
System.out.println("g()");
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
class Manager {
static NotifyDest notifyDest = new NotifyDest();
}
class Manager1 extends Manager implements Runnable{
public void run() {
notifyDest.f();
}
}
class Manager2 extends Manager implements Runnable{
public void run() {
notifyDest.g();
}
}
public class NotifyDemo {
public static void main(String[] args) throws Exception {
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Manager2());
TimeUnit.SECONDS.sleep(1);
exec.execute(new Manager1());
TimeUnit.SECONDS.sleep(1);
System.out.println("main()");
}
}
运行结果:
f()
exit
g()
main()
由运行结果可知,System.out.println(“exit”)语句在notifyAll()语句之后,但它执行了,说明线程调用notify()方法时,并没有立即释放锁并结束当前线程
。
注意:你必须用一个检查感兴趣的条件的while循环包围wait()。原因如下:
(1)你可能有多个任务出于相同的原因(即进入wait状态的条件一致)在等待同一个锁,而首先被唤醒任务可能会改变这个条件,那么这个任务应该被再次挂起,直至其感兴趣的条件发生变化。
(2)在这个任务从其wait()中被唤醒的时刻,有可能会有某个其他的任务已经做出了改变,而使得这个任务在此时不能执行。此时,应该通过再次调用wait()来将其重新挂起。
(3)也有可能某些任务出于不同的原因在等待你的对象上的锁(在这种情况下必须使用notifyAll())。在这种情况下,你需要检查是否已经由正确的原因唤醒,如果不是,就再次调用wait()。