线程通信
JDK 对于需要多线程写作完成的场景,提供对应API 支持
多线程协作场景:生产者 - 消费者模型。 (线程阻塞、线程唤醒)
使用方式: wait / notify 和 park / unpark
wait / notify
只能由同一对象锁的持有者线程调用,也就是写在同步块里面,否则会抛出IllegalMointorStateException 异常;
wait 方法导致当前线程等待,加入该对象的等待集合中,并放弃当前持有的对象锁;
notify / notifyAll 方法唤醒一个或所有正等待该对象锁的线程
注: 虽然wait自动解锁,但对顺序由要求,如果notify 被调用后才开始wait 方法线程会永远处于waiting 状态
public class WaitDemo{
public static Object target = null;
@Test
public void waitNotifyTest() throws Exception {
new Thread(() -> {
if (target == null) {
synchronized (this) {
try {
System.out.println("1、进入等待。");
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
System.out.print("2、获取目标,完成。");
}).start();
Thread.sleep(3000L);
target = new Object();
synchronized (this) {
this.notifyAll();
System.out.println("3、通知消费者。");
}
}
}
运行结果
1、进入等待。 3、通知消费者。 2、获取到目标,完成,
执行顺序

Park / Unpark
线程调用park,则等待“许可”,unpark 为指定线程提供“许可”
不要求 park 和 unpark 调用顺序
demo
@Test
public void parkUnparkTest() throws Exception {
Thread consumerTread = new Thread(() -> {
if (target == null) {
System.out.println("1、进入等待");
LockSupport.park();
}
System.out.println("2、获取到目标,完成");
});
consumerTread.start();
Thread.sleep(3000L);
target = new Object();
LockSupport.unpark(consumerTread);
System.out.println("3.通知消费者。");
}
运行结果
1、进入等待 3.通知消费者。 2、获取到目标,完成
伪唤醒问题
之前代码用if 语句判断时候进入等待状态,是错误的
官方建议在循环中检查等待条件,原因是处于等待状态的线程可能会受到错误唤醒,如果不在循环中检查等待条件,程序会在没有满足的结束条件情况下退出;
伪唤醒是指并非因为notify, notifyall,unpark等api调用唤醒,而是底层原因

导致死锁
public void parkUnparkDeadLockTest() throws Exception {
Thread consumerThread = new Thread(() -> {
System.out.println("1.1、进入等待。");
if (target == null) {
System.out.println("1.2、进入等待。");
synchronized (this) {
LockSupport.park();
}
}
System.out.println("2.买到包子,回家。");
});
consumerThread.start();
target = new Object();
synchronized (this) {
LockSupport.unpark(consumerThread);
}
System.out.println("3.通知消费者。");
}