线程通信
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.通知消费者。"); }