Park&UnPark

4.8Park&UnPark

基本使用
  • 它们是 LockSupport 类中的方法
// 暂停当前线程
LockSupport.park();

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

先 park 再 unpark

@Slf4j
public class ParkTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {

            log.debug("start...");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("park...");
            LockSupport.park();
            log.debug("resume...");

        }, "t1");
        t1.start();

        Thread.sleep(2000);
        log.debug("unPark...");
        LockSupport.unpark(t1); // 叫醒t1线程(t1线程先进入了park)

    }
}

/*输出结果*/
/*
16:16:30.036 [t1] DEBUG com.zzhua.test11.ParkTest - start...
16:16:31.040 [t1] DEBUG com.zzhua.test11.ParkTest - park...
16:16:32.034 [main] DEBUG com.zzhua.test11.ParkTest - unPark...
16:16:32.034 [t1] DEBUG com.zzhua.test11.ParkTest - resume...
*/

先 unpark 再 park

@Slf4j
public class ParkTest {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {

            log.debug("start...");
            try {
                Thread.sleep(2000);     // 睡眠2秒,目的是测试先进行unPark
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            log.debug("park...");
            LockSupport.park();
            log.debug("resume...");

        }, "t1");
        t1.start();               // 注意这里是在t1线程调用了start方法后, 再unPark t1的
                                  //     如果再t1线程调用star方法之前,调用unpark t1, 结果又不一样了
        Thread.sleep(1000);
        log.debug("unPark...");
        LockSupport.unpark(t1);  // 这里先unPark, 在t1线程将来调用LockSupport.park()时,忽略并直接运行(看输出)
                                 //    提前unPark只能消除将来的一次park,如果将来>=2次调用LockSupport.park()
    }                            //                                 那么,会park
}                                //    提前多次调用UnPark也只能消除将来的一次park,如果将来>=2次调用     
                                 //                                  LockSupport.park(),也会park
/*输出结果*/
/*
16:18:32.653 [t1] DEBUG com.zzhua.test11.ParkTest - start...
16:18:33.651 [main] DEBUG com.zzhua.test11.ParkTest - unPark...
16:18:34.656 [t1] DEBUG com.zzhua.test11.ParkTest - park...
16:18:34.656 [t1] DEBUG com.zzhua.test11.ParkTest - resume...
*/

再来看两个示例

涉及到LockSupport与start方法调用先后的关系,

通过这两个示例可以得出结论:我们可以使用LockSupport.unPark(指定线程)方法来精确的唤醒某个已经开启了的线程

@Slf4j
public class ParkTest {
    public static void main(String[] args) throws InterruptedException {


        Thread t1 = new Thread(() -> {

            log.debug("t1进入park");
            LockSupport.park();
            log.debug("t1继续运行");

        });
        t1.start();                   // 先start t1,再unPark
        LockSupport.unpark(t1);
        log.debug("main...");

    }
}
/*输出结果*/
/*
17:25:49.286 [main] DEBUG com.zzhua.test11.ParkTest - main...
17:25:49.286 [Thread-0] DEBUG com.zzhua.test11.ParkTest - t1进入park
17:25:49.288 [Thread-0] DEBUG com.zzhua.test11.ParkTest - t1继续运行
*/
@Slf4j
public class ParkTest {
    public static void main(String[] args) throws InterruptedException {


        Thread t1 = new Thread(() -> {

            log.debug("t1进入park");
            LockSupport.park();
            log.debug("t1继续运行");

        });
        LockSupport.unpark(t1); // 在调用t1的start方法之前, 先调用unPark , 再调用start
        t1.start();             // 通过输出结果可以看出,t1没有继续运行,
        log.debug("main...");   // 所以LockSupport应该在调用start方法之后被调用

    }
}
/*输出结果*/// 线程停住,没有结束
/*
17:26:57.797 [main] DEBUG com.zzhua.test11.ParkTest - main...
17:26:57.797 [Thread-0] DEBUG com.zzhua.test11.ParkTest - t1进入park
*/
特点
  • 与 Object 的 wait & notify 相比
    • wait,notify 和 notifyAll 必须配合 Object Monitor 一起使用,而 park,unpark 不必
    • park & unpark 是以线程为单位来【阻塞】和【唤醒】线程,而 notify 只能随机唤醒一个等待线程,notifyAll是唤醒所有等待线程,就不那么【精确】
    • park & unpark 可以先 unpark,而 wait & notify 不能先 notify
*原理之Park&unPark

每个线程都有自己的一个 Parker 对象,由三部分组成 _counter , _cond 和 _mutex 打个比喻

  • 线程就像一个旅人,Parker 就像他随身携带的背包,条件变量就好比背包中的帐篷。_counter 就好比背包中的备用干粮(0 为耗尽,1 为充足)
  • 调用 park, 就是要看需不需要停下来歇息
    • 如果备用干粮耗尽,那么钻进帐篷歇息
    • 如果备用干粮充足,那么不需停留,继续前进,并消耗干料
  • 调用 unpark,就好比令干粮充足
    • 如果这时线程还在帐篷,就唤醒让他继续前进,并消耗干粮
    • 如果这时线程还在运行,那么下次他调用 park 时,仅是消耗掉备用干粮,不需停留继续前进,因为背包空间有限,多次调用 unpark 仅会补充一份备用干粮
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值