众所周知wait和notifyObject中的方法,也就是说在JDK中每一个类都拥有这两个方法,其中wait有三个重载的方法。
Wait
public final void wait() throws InterruptedException;永不超时
public final void wait(long timeout) throws InterruptedException;当前线程进入阻塞,知道其他线程调用notify/notifyAll或者阻塞时间到达了timeout自动唤醒
public final void wait(long timeout,int nanos) throws InterruptedException;
1、wait方法必须拥有该对象的monitor,也就是说wait方法必须在同步方法中使用
2、当线程执行了该对象的waitf方法之后,就会放弃该对象的monitor的所有权并且进入该对象关联的wait Set中,也就是说一旦线程执行了某个对象的wait方法之后,就会放弃该对象的monitor的所有权,其它线程就有机会去争抢该对象的monitor所有权
Notify
public final native void notify();
1、唤醒单个正在执行该对象的wait方法的线程(sleep并不能唤醒)
2、如果某个线程执行了该对象的wait方法二进入阻塞则会被唤醒,没有则忽略
3、被唤醒的线程需要重新获取该对象所关联的monitor的lock才能继续执行
两者注意事项:
1、wait方法是可中断的方法,也就意味着线程一旦调用了wait方法进入阻塞,其它线程是可以使用interrupt方法将其打断的,此时会进入interruptedException异常,同时interrupt标识也会被擦除
2、线程执行了某个对象的wait方法之后,会加入与之对应的wait set中,每一个对象的monitor都有一个与之关联的wait set,线程进入wait set之后notify方法可以将其唤醒,也就是从wait set中弹出,同时中断wait中的线程也会将其唤醒。
3、必须在同步方法中使用wait和notify,因为执行wait和notify的前提条件是必须持有同步方法的monitor的所有权,运行下面任何一个方法都会抛出非法monitor状态异常IllegalMonitorStateException
//没在同步方法啊中调用wait/notify方法
private void test(){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
4、同步代码块中或同步方法中必须与wait notify方法的对象一致,简单的来说就是对哪个对象的monitor的进行同步,就只能对该对象进行wait或notify操作。
//案例一:同步方法中调用wait monotor关联的对象不一致
public synchronized void test2(){
try {
Thread.currentThread().wait(3000);//this
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//案例二:关联monitor对象与wait和notify对象不一致
private Object Mut = new Object();
private syncronized void testWait(){
try{
Mut.wait();
}catch(InterruptedException e){
e.printStackTrance();
}
}
private Syncronized void testNotify(){
Mut.notify();
}
顺便说一下面试常问的面试题:wait和sleep的区别?
相同:1、wait和sleep方法都可以使线程进入阻塞状态,并且两者都是可中断的方法,被中断后会收到中断异常
不同:1、wait是Object方法,sleep是Thread特有的方法
2、wait方法需要在同步方法中执行,而sleep不要
3、线程在同步方法中执行sleep方法时,并不会释放monitor的锁,而wait会释放
4、sleep方法短暂休眠之后会主动退出阻塞,而wait方法之后(没指定时间)需要其他线程中断或者唤醒才能退出阻塞
顺便一提:wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于:
wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器