5.park和unpark

park和unpark

它们是 LockSupport 类中的方法

// 暂停当前线程
LockSupport.park(); 
// 恢复某个线程的运行
LockSupport.unpark(暂停线程对象)

简单使用

@Slf4j(topic = "c.TestPark")
public class TestPark {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("t1 start......");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.debug("do park");
            LockSupport.park();
            log.debug("resume......");
        }, "t1");
        t1.start();


        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        log.debug("unpark......");
        LockSupport.unpark(t1);

    }
}

输出:

10:28:29.950 c.TestPark [t1] - t1 start......
10:28:30.952 c.TestPark [main] - unpark......
10:28:31.957 c.TestPark [t1] - do park
10:28:31.957 c.TestPark [t1] - resume......

Process finished with exit code 0

特点

Objectwait & notify 相比

  • waitnotifynotifyAll 必须配合 Object Monitor 一起使用,而 parkunpark 不必
  • park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,就不那么【精确】
  • park & unpark 可以先 unpark,而 wait & notify 不能先 notify

park 和 unpark 原理

==每个线程都有自己的一个parker对象(底层c语言实现的)==,由三部分组成 _counter_cond_mutex

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

  • 调用park就是判断是否需要停下来休息
    • 如果备用干粮(_counter)耗尽,那么就钻进帐篷(条件变量)里休息
    • 如果备用干粮(_counter)充足,那么不需要停留,继续前进。
  • 调用 unpark,就好比干粮(_counter)充足
    • 如果这时线程还在帐篷,就唤醒让他继续前进
    • 如果这时线程还在运行,那么下次他调用 park 时,仅是消耗掉备用干粮,不需停留,继续前进 ,因为背包空间有限,多次调用 unpark 仅会补充一份备用干粮(_counter),也就是多次unpark后只会让紧跟着的一次park失效

先调用park 再调用unpark

  1. 当前线程调用 Unsafe_park() 方法
  2. 检查_counter,本情况为0,这时,获得_mutex互斥锁
  3. 线程进入_cond条件变量阻塞
  4. 设置_counter = 0

  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter1
  2. 唤醒 _cond 条件变量中的 Thread_0
  3. Thread_0 恢复运行
  4. 设置 _counter0

先调用unpark 再调用park

  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为 1
  2. 当前线程调用 Unsafe.park() 方法
  3. 检查 _counter ,本情况为 1,这时线程无需阻塞,继续运行
  4. 设置 _counter 为 0
@Slf4j(topic = "c.TestPark")
public class TestPark {

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("t1 start......");
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            log.debug("do park");
            LockSupport.park();
            log.debug("resume......");
        }, "t1");
        t1.start();


        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }

        log.debug("unpark......");
        LockSupport.unpark(t1);

    }
}

输出:

11:47:58.747 c.TestPark [t1] - t1 start......
11:47:59.750 c.TestPark [main] - unpark......
11:48:00.754 c.TestPark [t1] - do park
11:48:00.754 c.TestPark [t1] - resume......

Process finished with exit code 0
0%