park & unpark

基本使用

park 和 unpark 是 LockSupport 类中的方法

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

park&unpark 与 wait&notify 异同

  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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xUtIyKuf-1594824984643)(https://note.youdao.com/yws/api/personal/file/BE3303ECCF624BE8827DEB79AA3D04F2?method=download&shareKey=67b22596ce8f776c1383cd521263d20a)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MB7eD4Mu-1594824984646)(https://note.youdao.com/yws/api/personal/file/4A8C32B909BB425CBA381970F0219B14?method=download&shareKey=018c4ff85590c0825c73355e48840114)]

  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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8iMVcPZD-1594824984649)(https://note.youdao.com/yws/api/personal/file/76E41E68054946ACABCA2B3F8BDEB094?method=download&shareKey=e34e0e8d026a2c52b606979a9816b553)]

  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...
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值