每个实例都有个线程的休息室wait set。
Wait方法——把线程放入wait set
使用wait方法时,线程便进入wait set,假设现在已经执行如下语句:obj.wait();
则目前的线程停止执行,进入实例obj的的wait set.这个操作成为:
线程在obj上wait().(这个obj不是线程而是对象)
如果实例方法还有如下的语句时:wait();
则其意义同:this.wait();
故执行wait的线程就会进入this的wait set.此时就变成了在this上wait.
如欲执行wait()方法,线程需获取锁定(这是规则)。但是当线程进入wait set时,已经释放了该实例的锁定。
Notify方法——从wait set拿出线程
使用notify()(通知)方法时,可以从wait set拿出一个线程。
obj.notify();(这个obj也是对象,不是线程)则从wait set里的线程中挑出一个,唤醒这个线程。被唤醒的线程便退wait set
线程A想紧接着wait之后的代码执行,但是现在线程B拿到了锁。
Notify后的线程
被notify唤醒的线程不是在notify后立即执行,因为在notify的那一刻,执行notify 的线程还握着锁定不放,所以其他线程无法获取该实例的锁定。
Notify如何选择线程
假设执行notify方法时,wait set里面正在执行的线程不止一个。规格并没有注明此时该选择哪一个线程。究竟是选择等待线程里面的第一个,随机选择或是另以其他方式选择,则以java处理系统而异。
notifyAll()方法——从wait set 拿出所有线程
使用notifyAll(通知全体)方法时,会将所有在waitset苦等的线程都拿出来。
obj.notifyAll()则会唤醒所有留在
实例obj的wait set里的线程。
而notifyAll();则其意义同this.notifyAll();故这个语句所在方法的实例(this)的wait set里的线程会全部放出来。
跟wait方法和notify方法一样,线程必须要获取要调用实例的锁定,才能调用notifyAll方法。
被唤醒的线程便开始去获取刚才wait时释放掉的锁定,那么现在这个锁定现在是在谁的手中呢?
没错,锁定就是在刚才执行notifyAll方法的程序手里,因此即使所有线程都退出了wait set,但他们仍然在去获得锁定的状态下,还是有阻挡
。要等到刚才执行notifyAll方法的线程释放出锁定后,其中一名幸运儿才会实际执行。
要是没有锁定呢
若没有锁定的线程去调用wait,notify或notifyAll时,便会抛出异常java.lang.IllegalMonitorStateException.
调用notify方法还是notifyAll方法
Notify方法和notifyAll方法两者非常相似,到底该用哪一个?老实说,这个选择有点难。选择notify的话,因为要唤醒的线程比较少,程序处理速度当然要比notifyAll略胜一筹。但是选择notify时,若这部分程序处理的不好,可能会有程序挂掉的危险性,一般说来,选择notifyAll所写出来的程序代码要比选择notify可靠。除非你能确定程序员对程序代码的意义和能力限度一清二楚,否则选择notifyAll应该比较稳扎稳打
wait,notify,notifyAll是Object类的方法
obj.wait()是把现在的线程放到obj的wait set
obj.notify()是从obj的wait set里唤醒一个线程
obj.notifyAll()是唤醒所有在obj的wait set里的线程
换句话说,把wait、notify、notifyAll三者均解释为对实例对象的wait set的操作,会比说他们是对线程的操作更贴切,由于所有实例都会有wait set,所以wait、notify、notifyAll才会是Object类的方法。
虽然三者不是Thread类固有的方法,不过,因为Object类是Java所有类的祖先类,所以wait、notify、notifyAll也是Thread的方法。