wait(),notify()和notifyall()方法是java.lang.Object类为线程提供的用于实现线程间通信的同步控制方法。
wait()方法使当前线程主动释放互斥锁,并进入该互斥锁的等待队列。(也就是说,它使当前线程暂停执行,
等待其他线程执行notify()方法或者notifyall()方法后再继续执行本线程。)
notify()系列方法,
用于释放一个项目的线程,唤醒另一个可能在等待的线程。
线程等待有两种调用格式:
1.wait()等待通信线程唤醒后再继续执行本线程。
2.wait(long millis)等待通信线程唤醒或者最多等待millis毫秒后,再继续执行本线程。
调用
wait()和notify()
系列方法时,当前线程必须拥有此对象监视器(即
对象锁
)。如果当前线程不是此对象监视器的所有者,会抛IllegalMonitorStateException。
通过以下三种方法之一,线程可以成为对象监视器的所有者:
* 通过执行此对象的同步实例方法。
* 通过执行在此对象上进行同步的 synchronized 语句的正文。
* 对于 Class 类型的对象,可以通过执行该类的同步静态方法。
注意1:对于一个对象,某一时刻只能有一个线程拥有该对象的监视器。
public final void
wait()
throws InterruptedException
在其他线程调用此对象的 notify() 方法或 notifyAll() 方法前,导致当前线程等待。换句话说,此方法的行为就好像它仅执行 wait(0) 调用一样。
当前线程必须拥有此对象监视器。该线程发布对此监视器的所有权并等待,直到其他线程通过调用 notify 方法,
或 notifyAll 方法通知在此对象的监视器上等待的线程醒来。然后该线程将等到重新获得对监视器的所有权后才能继续执行。
对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用:
synchronized
(obj) {
while
(<condition does not hold>)
obj.wait();
.
.. // Perform action appropriate to condition
}
此方法只应由作为此对象监视器的所有者的线程来调用。
抛出:
IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。
InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。
在抛出此异常时,当前线程的中断状态 被清除。
注意1:wait()方法后,当前线程暂停执行,线程进入Blocked状态,
cpu不会分给其时间,
等待其他线程执行notify()方法或者notifyall()方法或用interrupt()取消后再继续执行本线程
它还有种调用格式:
wait(long millis)等待通信线程唤醒或者最多等待millis毫秒后,再继续执行本线程。
注意2:wait()方法使当前线程主动
释放互斥锁.
注意3:线程A调用了wait()进入了等待状态,也可以用
interrupt()取消.详细参考《线程的interrupt》
注意4:对于某一个参数的版本,实现中断和虚假唤醒是可能的,而且此方法应始终在循环中使用。
public final void notify()
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。
选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;
例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
注意1:被notify()唤醒的线程,也只有在
当前线程放弃此对象上的锁定后,才可能
被执行,
(他需要以常规方式与在该对象上主动同步的其他所有线程进行锁竞争,竞争成功了还能被执行)
public final void notifyAll()
唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个 wait 方法,在对象的监视器上等待。
直到当前线程放弃此对象上的锁定,才能继续执行被唤醒的线程。被唤醒的线程将以常规方式与在该对象上主动同步的其他所有线程进行竞争;
例如,唤醒的线程在作为锁定此对象的下一个线程方面没有可靠的特权或劣势。
此方法只应由作为此对象监视器的所有者的线程来调用。
抛出:
IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。
注意1:被唤醒的线程,也只有在当前线程放弃此对象上的锁定后,才可能被执行,
(他需要以常规方式与在该对象上主动同步的其他所有线程进行锁竞争,竞争成功了还能被执行)
wait(),notify(),notifyall()所在对象和锁是绑定的(他们是一一对应的,即一个锁只对应对象,一个对象只对应锁)。
因此它的wait(),notify(),notifyall()都是操作同一个对象。
但是我们只想通过notify(),notifyall()来对一些wait()线程而不是该锁上所有wait()的线程进行唤醒时。(这样的效率要高很多,当然应该不只是可以得到效率的好处)
synchronized和wait(),notify(),notifyall()就显得苍白无力。
幸好java1.5中提供了ReentrantLock,ReadLock,WriteLock来和Condition配套使用以实现这种功能。
示例1
class
BoundedBuffer {
final String[] items = new String[100];
int putptr, takeptr, count;
final String conditionPut="put";
final String conditionTake="take";
synchronized public void
put
(String x) throws InterruptedException {
while (count == items.length)
{
System.out.println("It is full.the action of put is wait");
synchronized
(conditionPut)
{
conditionPut.wait();
//@1
}
}
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
synchronized
(conditionTake)
{
conditionTake.notify();
}
}
synchronized public
String
take()
throws InterruptedException {
while
(count == 0)
{
System.out.println("It is empty.the action of take is wait");
synchronized
(conditionTake)
{
conditionTake.wait();
//@1
}
}
String x = items[takeptr];
if
(++takeptr == items.length) takeptr = 0;
--count;
synchronized
(conditionPut)
{
conditionPut.notify();
}
return x;
}
}
注意:在
@1处无法进行当前线程对this的锁的释放,当然别的线程也就无法进入put和take,
线程就死锁了。
实例1:
因为
wait(),
notify(),
notifyall()所在对象和锁是绑定的
notify(),notifyall()是对对象(锁)上所有wait()的线程进行唤醒(notify是随机选一个)。
所以只能用下面的方式。
class
BoundedBuffer
{
final String[] items = new String[100];
int putptr, takeptr, count;
synchronized public void
put(String x) throws InterruptedException {
while
(count == items.length)
@5
{
System.out.println("It is full.the action of put is wait");
this.wait();
//@1
}
items[putptr] = x;
if (++putptr == items.length)
{
putptr = 0;
}
++count;
this.notify();
@2
}
synchronized publi
c String
take()
throws InterruptedException {
while
(count == 0)
//@6
{
System.out.println("It is empty.the action of take is wait");
this.wait();
//@3
}
String x = items[takeptr];
if
(++takeptr == items.length)
{
takeptr = 0;
}
--count;
this.notify();
//@4
return x;
}
}
注意:
虽然我们希望的是@1处由@4出来唤醒,@3处由@2出来唤醒。
但是
notify()
,
notifyall()
是对锁上所有
wait()
的线程进行唤醒(notify是随机选一个)。
因此
@2
出也可能唤醒
@1
,所以
@5
出要用
while
来判断是否真的满足条件了,
@6
同理。