Skip to content

Park 和 Unpark

基本使用

park 和 unpark 是 LockSupport 类中的静态方法

// 暂停当前线程
LockSupport.park();

// 恢复某个线程的运行
LockSupport.unpark();

示例:

public class TestParkUnpark {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                System.out.println("开始执行...");
                Thread.sleep(1000);
                System.out.println("park...");
                LockSupport.park();
                System.out.println("从 park 处继续执行...");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, "t1");
        t1.start();
        Thread.sleep(3000);
        System.out.println("unpark...");
        LockSupport.unpark(t1);
    }
}
14:07:48.762 [t1] INFO com.tcmyxc.juc.TestParkUnpark - 开始执行...
14:07:49.781 [t1] INFO com.tcmyxc.juc.TestParkUnpark - park...
14:07:51.770 [main] INFO com.tcmyxc.juc.TestParkUnpark - unpark...
14:07:51.770 [t1] INFO com.tcmyxc.juc.TestParkUnpark - 从 park 处继续执行...

park 之后的线程状态是 wait

如果先调用 unpark,再调用 park,那就会直接往下面执行(相当于运行需要满足一定的条件,调用的时候发现已经满足了,那就直接接着运行)

示例:

public static void main(String[] args) throws InterruptedException {
    Thread t1 = new Thread(() -> {
        try {
            log.info("开始执行...");
            Thread.sleep(3000);
            log.info("park...");
            LockSupport.park();
            log.info("从 park 处继续执行...");
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }, "t1");
    t1.start();
    Thread.sleep(1000);
    log.info("unpark...");
    LockSupport.unpark(t1);// 主线程先 unpark
}
14:18:53.385 [t1] INFO com.tcmyxc.juc.TestParkUnpark - 开始执行...
14:18:54.384 [main] INFO com.tcmyxc.juc.TestParkUnpark - unpark...
14:18:56.390 [t1] INFO com.tcmyxc.juc.TestParkUnpark - park...
14:18:56.390 [t1] INFO com.tcmyxc.juc.TestParkUnpark - 从 park 处继续执行...

特点

  • park & unpark 是以线程为单位来阻塞和唤醒线程的,精确到了具体的线程
  • unpark 可以先于 park 使用

原理

每个线程都有自己的一个 Parker 对象,由三部分组成 _counter_cond_mutex

线程就像一个旅行者,Parker 就像他随身携带的背包,条件变量 _mutex 就好比背包中的帐篷。_counter 就好比背包中的备用干粮(0为耗尽,1为充足)

调用park就是累了吃备用干粮。如果备用干粮耗尽,那么钻进帐篷歇息;如果备用干粮充足,那么不需停留,继续前进

调用unpark,就好比别人送来了干粮。如果这时线程在帐篷休息,就唤醒让他继续前进;如果这时线程还在运行,那么下次他调用park时,仅是消耗掉备用干粮,不需停留继续前进。因为背包空间有限,多次调用unpark仅会补充一份备用干粮

类似0-1信号量

调用 park 设置 _counter 为0

调用 unpark 设置 _counter 为1