Wait
和 Notify
是 Java
面试中常见的问题,可是在平时工做中可能不常见到。你们或多或少知道些背景知识,例如两者均为 Object
类的方法,而不是 Thread
特有的(由于锁
是每一个对象都具备的特性,所以操做锁的方法也紧跟对象,没毛病),且都只能在同步代码块
中调用(即前提是先得到对象的监视器锁
,通常来讲在 synchronized
代码块中使用),不然抛出异常 IllegalMonitorStateException
。java
Wait
会挂起本身让出 CPU
时间片,并将自身加入锁定对象的 Wait Set
中,释放对象的监视器锁(monitor)
让其余线程能够得到,直到其余线程调用此对象的 notify( )
方法或 notifyAll( )
方法,自身才能被唤醒(这里有个特殊状况就是 Wait
能够增长等待时间);Notify
方法则会释放监视器锁的同时,唤醒对象 Wait Set
中等待的线程,顺序是随机的不肯定。程序员
虚拟机规范
中定义了一个 Wait Set
的概念,但至于其具体是什么样的数据结构规范没有强制规定,意味着不一样的厂商能够自行实现,但无论怎样,线程调用了某个对象的 Wait
方法,就会被加入该对象的 Wait Set
中面试
下面经过一段 demo
来解释 Wait
和 Notify
的功能shell
import java.util.concurrent.TimeUnit;
public class WaitNotify {
public static void main(String[] args) {
final Object A = new Object();
final Object B = new Object();
Thread t1 = new Thread("t1-thread") {
@Override
public void run() {
synchronized (A) {
System.out.println(Thread.currentThread().getName() + "拿到 A 的监视器锁");
System.out.println(Thread.currentThread().getName() + "尝试获取 B 的监视器锁");
try {
System.out.println(Thread.currentThread().getName() + "休眠 2s,不释放 A 的监视器锁");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "挂起本身,释放 A 的监视器锁");
A.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒,等待获取 B 的监视器锁");
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println(Thread.currentThread().getName() + "拿到 B 的监视器锁");
B.notify();
}
}
}
};
Thread t2 = new Thread("t2-thread") {
@Override
public void run() {
synchronized (B) {
System.out.println(Thread.currentThread().getName() + "拿到 B 的监视器锁");
System.out.println(Thread.currentThread().getName() + "尝试获取 A 的监视器锁");
synchronized (A) {
System.out.println(Thread.currentThread().getName() + "拿到 A 的监视器锁");
try {
System.out.println(Thread.currentThread().getName() + "休眠 2s,不释放 A 的监视器锁");
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "挂起本身,释放 A 的监视器锁,唤醒 t0");
A.notify();
}
try {
System.out.println(Thread.currentThread().getName() + "休眠 2s,不释放 B 的监视器锁");
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "挂起本身,释放 B 的监视器锁");
B.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
t1.start();
t2.start();
}
}
复制代码
t1-thread拿到 A 的监视器锁
t2-thread拿到 B 的监视器锁
t1-thread尝试获取 B 的监视器锁
t2-thread尝试获取 A 的监视器锁
t1-thread休眠 2s,不释放 A 的监视器锁
t1-thread挂起本身,释放 A 的监视器锁
t2-thread拿到 A 的监视器锁
t2-thread休眠 2s,不释放 A 的监视器锁
t2-thread挂起本身,释放 A 的监视器锁,唤醒 t0
t2-thread休眠 2s,不释放 B 的监视器锁
t1-thread被唤醒,等待获取 B 的监视器锁
t2-thread挂起本身,释放 B 的监视器锁
t1-thread拿到 B 的监视器锁
t2-thread被唤醒
Process finished with exit code 0
复制代码
这是一个不定时更新的、披着程序员外衣的文青小号,欢迎关注。数据结构