1.wait()、notify()和notifyAll()是Object类的final方法,无法被重写。
2.某个对象要调用wait()、notify()和notifyAll()都要写在同步代码块或者同步方法里面,且当前线程必须有此对象的锁。
3.调用某个对象的wait()是使当前拥有那个对象的线程挂起,暂停执行线程后面的代码,直到被另外一个线程唤醒进入等锁池,通过JVM调度获得对象的锁后才继续执行未执行完的代码。
4.某个对象的notify()方法唤醒在此对象监视器上的某个线程,具体哪一个是由JVM决定,而notifyAll()是唤醒在此对象监视器上的全部线程。
下面通过一个例子分析:
class NumberPrint implements Runnable{
private int number;
public byte res[];
public static int count = 5;
public NumberPrint(int number, byte a[]){
this.number = number;
res = a;
}
public void run(){
synchronized (res){
while(count-- > 0){
try {
res.notify();//唤醒等待res资源的线程,把锁交给线程(该同步锁执行完毕自动释放锁)
System.out.println(" "+number);
res.wait();//释放CPU控制权,释放res的锁,本线程阻塞,等待被唤醒。
System.out.println("------线程"+Thread.currentThread().getName()+"获得锁,wait()后的代码继续运行:"+number);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}//end of while
return;
}//synchronized
}
}
public class WaitNotify {
public static void main(String args[]){
final byte a[] = {0};//以该对象为共享资源
new Thread(new NumberPrint((1),a),"1").start();
new Thread(new NumberPrint((2),a),"2").start();
}
}
输出结果:
1
2
------线程1获得锁,wait()后的代码继续运行:1
1
------线程2获得锁,wait()后的代码继续运行:2
2
------线程1获得锁,wait()后的代码继续运行:1
1
------线程2获得锁,wait()后的代码继续运行:2
一般情况下线程1会先启动获得res的锁(实际上不确定),线程2启动之后进入就绪状态等待线程1释放res的锁,线程1进入循环代码,先唤醒等待res线程的资源,输出1,然后调用res.wait(),释放CPU控制权,释放res的锁,挂起线程1,等待被唤醒。
然后线程2获得了res的锁进入自己线程的循环代码,调用res.notify()唤醒了线程1,输出2后把线程2挂起,释放CPU控制权和res的锁,等待被唤醒。
线程1获得了res的锁后继续后续的循环代码,先输出-------线程1获得锁,wait()后的代码继续运行:1,再执行第二次循环,唤醒线程2,输出1,再把自己挂起,释放资源。
线程2获得了res的锁后继续自己后续的循环代码,输出-------线程2获得锁,wait()后的代码继续运行:2再执行第二此循环,唤醒线程1,输出2,再把自己挂起,释放资源。
2个线程按照上述步骤依次获得资源,相继输出后面的内容。
总结:
wait()、notify和notifyAll()这些方法必须在同步代码块或者同步方法里面使用,且必须是被加锁的对象来调用。从功能上来说wait()线程主动释放CPU控制权,主动释放对象锁,同时本线程暂停执行。直到有其它线程调用加锁对象的notify()或者notifyAll()唤醒该线程,才能继续获取对象锁,并继续执行剩下的代码。这样就提供了在线程间同步、唤醒的操作。