park & unpark

基本使用

park 和 unpark 是 LockSupport 类中的方法

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

park&unpark 与 wait¬ify 异同

  1. park和wait都会让线程进入WAITINGTIMED_WAITING状态;
  2. wait、notify 和 notifyAll 必须配合 Object Monitor 一起使用;而 park、unpark 不必,park没有锁的概念。
    • 我们知道wait必须写在synchronized里,这是因为wait必须是由加锁的对象调用的,其原理是通过加锁的对象关联的同步监视器(Monitor)来管理线程,当调用wait方法时,把当前持锁线程放入Monitor的WaitSet,所以无论是wait还是notify都必须由被锁对象调用。
    • 而park则不同,park是类方法,调用方式类似于sleep,而unpark必须指定线程对象,所以它是“精确唤醒”。
  3. park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,wait和notify都是以被锁对象(准确讲是其关联的Monitor)为单位的,所以 notify 只能随机唤醒一个关联的Monitor里等待的线程,notifyAll 是唤醒关联的Monitor里所有的等待的线程,就不那么【精确】 。
  4. park & unpark 可以先 unpark,而 wait & notify 不能先 notify。

park & unpark底层原理

设计思想

park是在怎么实现精准唤醒的哪,其实并不难理解,我们知道wait是基于锁的,所以它有竞争,所以搞了个Monitor管理被锁对象的这些个线程,但park并没有锁的概念,一个线程就一个Parker对象,你把我park了(类似于sleep)我就暂停呗,想把我唤醒就必须精确到我现在这一个线程对象。

底层原理

在具体底层的原理上,每个线程都有自己的一个 Parker 对象,由三部分组成 _counter_cond_mutex。介绍一下上面第4点为啥可以先 unpark。

  • case1: park -> unpark

park & unpark_第1张图片

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

park & unpark_第2张图片

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

例子:

public class TestParkUnpark {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("start...");
            sleep(1);
            log.debug("park...");
            LockSupport.park();
            log.debug("resume...");
        }, "t1");
        t1.start();

        sleep(2);
        log.debug("unpark...");
        LockSupport.unpark(t1);
    }
}

/*Output:
13:17:15.621 c.TestParkUnpark [t1] - start...
13:17:16.626 c.TestParkUnpark [t1] - park...
13:17:17.618 c.TestParkUnpark [main] - unpark...
13:17:17.618 c.TestParkUnpark [t1] - resume...
*/
  • case2: unpark -> park

park & unpark_第3张图片

  1. 调用 Unsafe.unpark(Thread_0) 方法,设置 _counter 为1,发现线程正在运行,则无需唤醒
  2. 当前线程调用 Unsafe.park() 方法
  3. 检查 _counter ,本情况为1,这时线程无需阻塞,继续运行
  4. 设置 _counter 为 0

例子:

public class TestParkUnpark {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.debug("start...");
            sleep(2);
            log.debug("park...");
            LockSupport.park();
            log.debug("resume...");
        }, "t1");
        t1.start();

        sleep(1);
        log.debug("unpark...");
        LockSupport.unpark(t1);
    }
}

/*Output:
13:18:31.336 c.TestParkUnpark [t1] - start...
13:18:32.334 c.TestParkUnpark [main] - unpark...
13:18:33.344 c.TestParkUnpark [t1] - park...
13:18:33.344 c.TestParkUnpark [t1] - resume...
*/

你可能感兴趣的:(Java并发编程,java,多线程,并发编程)