基本使用
它们是 LockSupport 类中的方法
// 暂停当前线程
LockSupport.park();
// 恢复某个线程的运行
LockSupport.unpark(暂停的线程对象);
先 park 再 unpark
public class Demo1 {
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
System.out.println(DateUtil.getCurrentTime() + " " + Thread.currentThread().getName() + " start......");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(DateUtil.getCurrentTime() + " " + Thread.currentThread().getName() + " park");
LockSupport.park();
System.out.println(DateUtil.getCurrentTime() + " " + Thread.currentThread().getName() + " resume");
}, "t1");
t1.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(DateUtil.getCurrentTime() + " " + Thread.currentThread().getName() + " unpark......");
LockSupport.unpark(t1);
}
}
特点
与 Object 的 wait & notify 相比
- wait ,notify,notifyAll 必须配合 Object Monitor 一起使用,而 park unpark 不必
- park unpark 更精确,以线程为单位来【阻塞】和【唤醒】线程,而 notify notifyAll 只能随机唤醒一个或全部唤醒
- park unpark 没有调用的先后顺序,而 wait & notify 只能先调 wait 再调 notify
原理
每个线程都有自己的一个 Parker 对象(底层,C语言),由三部分组成_counter,_cond(条件变量),_mutex 。比喻
image.png
image.png
- 当前线程调用 Unsafe.park() 方法
- 检查_counter,本情况为 0 ,这时,获得 _mutex互斥锁(_mutex 也是一个阻塞队列)
- 线程进入 _cond 条件变量阻塞
- 设置 _counter = 0(即使原来就是 0 也要设置)
image.png
- 调用 Unsafe.unpark(Thread_0)方法,设置 _counter = 1
- 唤醒 _cond 条件变量中的 Thread_0
- Thread_0 恢复运行
- 设置 _counter = 0
image.png
- 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter = 1
- 当前线程调用 Unsafe.park() 方法
- 检查 _counter,本情况为 1,这时线程无需阻塞,继续运行
- 设置 _counter = 0