注意点:
wait(),notify(),notifyAll()方法
(1)在语法上,都必须在synchronized代码块中,如果不在synchronized代码块中,就会抛出异常。
(2)由于在synchronized代码块中,所以当前线程一定是处于运行态的,且成功获取对象锁的。
wait()方法
- 使当前线程停止运行
- 当前线程释放持有的对象锁,并由运行态转变为阻塞态(具体为:等待/超时等待,进入等待队列)。直到其他线程调用此对象的notify或者notifyall方法,将当前线程唤醒(由阻塞态转变为就绪态)。
- wait()方法执行后,当前线程释放锁,线程与其他线程会再次竞争对象锁。
notify()/notifyAll()方法
- 使被停止线程继续运行
- notify():随机唤醒一个被wait方法阻塞的线程。
- notifyAll():唤醒所欲被wait方法阻塞的线程。
- 该方法会唤醒两个部分的阻塞线程:
(1)调用wait()释放对象锁的阻塞线程
(2)在synchronized竞争对象锁失败的线程
notify是将停止的线程唤醒,由于wait使线程处于阻塞态,所以notify会唤醒之前被wait阻塞的线程。又因为notify是在synchronized代码块结束后才唤醒线程,所以类似看起来是之前竞争对象锁失败的线程被notify唤醒,其实是因为synchronized代码块执行完毕,同步队列的线程本身就会被再次唤醒,与notify无关。
- 在调用notify方法后,当前线程不会马上释放锁
- 该线程的对象锁,要等到执行notify()方法的线程将程序执行完,也就是退出同步代码块后,才会释放对象锁。
用面包店的例子来说明什么时候该用wait()/notify()
代码:
/**
* 假设面包店有面包师傅生产面包,消费者消费面包
* 1.面包师傅有5个,可以一直生产面包,每人每次生产3个
* 2.消费者有20个,可以一直消费面包,每次消费1个
* 3.面包店库存的上限是100个,到达100个就不再生产,下限是0;
*/
public class SingnalTest {
//库存
private static int SUM;
public static void main(String[] args) {
//5个面包师傅同时启动
for (int i = 0; i < 5; i++) {
new Thread(() -> {
try {
while (true) {
//加锁保证线程安全,保证可见性 (类加锁)
synchronized (SingnalTest.class) {
if (SUM + 5 > 100) {
SingnalTest.class.wait();
} else {
SUM += 5;
System.out.println(Thread.currentThread().getName()+"生产了面包:库存:"+SUM);
Thread.sleep(500);
// SingnalTest.class.notify();//随机通知一个wait方法阻塞的线程
SingnalTest.class.notifyAll();//通知全部wait方法阻塞的线程
}
}
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"面包师傅【"+ i +"】").start();
}
//20个消费者,同时启动
for (int i = 0; i < 20; i++) {
new Thread(() -> {
try {
while (true) {
//加锁保证线程安全,保证可见性 (类加锁)
synchronized (SingnalTest.class) {
if (SUM -3 < 0) {
SingnalTest.class.wait();
} else {
SUM -= 3;
System.out.println(Thread.currentThread().getName()+"消费了面包:库存:"+SUM);
Thread.sleep(500);
// SingnalTest.class.notify();
SingnalTest.class.notifyAll();
}
}
Thread.sleep(200);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"消费者【"+ i +"】").start();
}
}
}
分析:
基于消费者代码进行分析:
wait和sleep方法的对比(面试题)
理论上而言,wait和sleep方法是完全没有可比性的,wait是属于线程间的通信,sleep是将线程阻塞一段时间,相同点就是可以让线程放弃执行一段时间。
面试时,可以回答:
- wait方法调用之前需要请求锁,(需要在synchronized代码块中)而wait执行时会先释放锁,等再次被唤醒时会重新再次请求锁。
- sleep方法无视锁的存在,即被调用时,之前请求的锁不会被释放,没有锁也不可以。
- wait是Object的方法,sleep是Thread的静态方法。