实现线程挂起和唤醒,并对比 Object#wait()、Thread#sleep()

在 AQS 或者 BlockingQueue 中常用的实现线程挂起和唤醒的方法就是 LockSupport 的 park 和 unpark 方法

park

LockSupport 类中提供的 park 相关方法有 6 个,根据是否指定 blocker 分为两类,指定 blocker 的优点在于排查问题的时候能够知道 park 的原因,所以推荐使用带有 blocker 的 park 方法

//不带blocker的方法
public static void park();
public static void parkNanos(long nanos);
public static void parkUntil(long deadline);
//带blocker的方法
public static void park(Object blocker);
public static void parkNanos(Object blocker, long nanos);
public static void parkUntil(Object blocker, long deadline);

unpark

将指定线程的许可置为可用,用于唤醒该线程

LockSupport.unpark(thread);

线程调用 park 时其实就是去获取许可,如果能成功获取到许可则能够往下执行,否则阻塞直到成功获取许可为止,而当线程调用 unpark 时则是释放许可,供线程去获取,park、unpark 方式的执行顺序不影响唤醒,也不会报错,也就是说可以先调用 unpark 再调用 park,此时的 park 不会阻塞:

LockSupport.unpark(Thread.currentThread());
LockSupport.park(BLOCKER);

对于这个许可,仅仅是「有」和「无」的区别,多次调用 unpark 并不会累加许可,park 方法会等待许可并释放掉许可,注意下面两个场景:

//先调用unpark两次,再调用park,线程不会阻塞
LockSupport.unpark(Thread.currentThread());
LockSupport.unpark(Thread.currentThread());
LockSupport.park(BLOCKER);
//先调用unpark两次,此时许可不会累加,仅表示有许可
LockSupport.unpark(Thread.currentThread());
LockSupport.unpark(Thread.currentThread());
//这里不会阻塞,会释放许可
LockSupport.park(BLOCKER);
//这里会阻塞,因为许可被上一行的park释放了,许可从「有」变成了「无」
LockSupport.park(BLOCKER);

对比