多線程之深入理解park與unpark

1.背景

面試官問,如何暫停一個線程勒.....

說說你對park的理解.......

2.代碼

package com.ldp.demo01;

import com.common.MyThreadUtil;
import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.LockSupport;

/**
 * @author 姿勢帝-博客園
 * @address https://www.cnblogs.com/newAndHui/
 * @WeChat 851298348
 * @create 02/01 10:58
 * @description <p>
 * park & unpark 與  wait & notify 相比
 * 1.wait,notify 和 notifyAll 必須配合 Object Monitor 一起使用,而 park,unpark 不必
 * 2.park & unpark 是以線程爲單位來 阻塞 和 喚醒 線程,而 notify 只能隨機喚醒一個等待線程,notifyAll 是喚醒所有等待線程,就不那麼 精確
 * 3.park & unpark 可以先 unpark,而 wait & notify 不能先 notify
 * </p>
 */
@Slf4j
public class Test04UnPark {
    /**
     * 1.先park在unPark
     * 2.先unPark 在park---(會導致第一次park無效)
     * <p>
     * park的理解(非常重要)
     * <p>
     * 每個線程都有自己的一個 Parker 對象,
     * 由三部分組成 _counter(計數器) , _cond (聚光器,這裏相當於休息室) 和 _mutex (互斥量)
     * <p>
     * 打個比喻
     * 線程就像一個旅人,Parker 就像他隨身攜帶的揹包, _cond好比揹包中的帳篷。_counter就好比揹包中的備用乾糧(0 爲耗盡,1 爲充足)
     * <p>
     * 調用 park 就是要看需不需要停下來歇息
     * 如果備用乾糧耗盡(_counter=0),那麼進入帳篷休息
     * 如果備用乾糧充足(_counter=1),那麼不需停留,繼續前進
     * <p>
     * <p>
     * 調用 unpark,就好比補充乾糧(不論調用多少次都只能補充一次)
     * 如果這時線程還在帳篷中休息,就喚醒讓他繼續前進
     * 如果這時線程正在運行中,那麼下次他調用 park 時,僅是消耗掉備用乾糧(即這次unpark補充的乾糧),不需停留繼續前進
     * 因爲揹包空間有限,多次調用 unpark 僅會補充一份備用乾糧
     * <p>
     * <p>
     * 直接解釋就是:
     * 每個線程都有自己的一個 Parker 對象,
     * 由三部分組成 _counter(計數器) , _cond (聚光器,這裏相當於休息室) 和 _mutex (互斥量)
     * <p>
     * <p>
     * 當調用 park想暫停線程時
     * 如果在這之前沒有調用unpark,就直接暫停當前線程.
     * 如果在這之前調用了unpark,線程繼續運行,相當於這個park無效.
     * <p>
     * <p>
     * 當調用 unpark想讓線程繼續運行時
     * 如果線程是處於暫停狀態,線程被喚醒開始執行;
     * 如果線程本來就處於運行狀態,線程繼續運行,並且會記住這次unpark,線程下次park時無效.
     * 多次調用unpark時僅一次unpark有效.
     *
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.info("t1......1");
            // 2秒後繼續執行
            MyThreadUtil.sleep(2);
            // 暫停當前線程
            LockSupport.park();
            log.info("t1......2");
            LockSupport.park();
            log.info("t1......3");
            LockSupport.park();
            log.info("t1......4");
        }, "t1");
        t1.start();
        // 2秒後繼續執行
        // MyThreadUtil.sleep(2);
        // 回覆運行中的狀態
        LockSupport.unpark(t1);
        log.info("unpark......1");
        LockSupport.unpark(t1);
        log.info("unpark......2");
    }
}

 

3.區別

park & unpark 與 wait & notify 相比

1.wait,notify 和 notifyAll 必須配合 Object Monitor 一起使用,而 park,unpark 不必

2.park & unpark 是以線程爲單位來 阻塞 和 喚醒 線程,而 notify 只能隨機喚醒一個等待線程,notifyAll 是喚醒所有等待線程,就不那麼 精確

3.park & unpark 可以先 unpark,而 wait & notify 不能先 notify

4.原理理解

每個線程都有自己的一個 Parker 對象,
由三部分組成 _counter(計數器) , _cond (聚光器,這裏相當於休息室) 和 _mutex (互斥量)

4.1.比喻理解

線程就像一個旅人,Parker 就像他隨身攜帶的揹包, _cond好比揹包中的帳篷。_counter就好比揹包中的備用乾糧(0 爲耗盡,1 爲充足)

調用 park 就是要看需不需要停下來歇息
如果備用乾糧耗盡(_counter=0),那麼進入帳篷休息
如果備用乾糧充足(_counter=1),那麼不需停留,繼續前進


調用 unpark,就好比補充乾糧(不論調用多少次都只能補充一次)
如果這時線程還在帳篷中休息,就喚醒讓他繼續前進
如果這時線程正在運行中,那麼下次他調用 park 時,僅是消耗掉備用乾糧(即這次unpark補充的乾糧),不需停留繼續前進
因爲揹包空間有限,多次調用 unpark 僅會補充一份備用乾糧

4.2.直接解釋

每個線程都有自己的一個 Parker 對象,
由三部分組成 _counter(計數器) , _cond (聚光器,這裏相當於休息室) 和 _mutex (互斥量)

當調用 park想暫停線程時
如果在這之前沒有調用unpark,就直接暫停當前線程.
如果在這之前調用了unpark,線程繼續運行,相當於這個park無效.


當調用 unpark想讓線程繼續運行時
如果線程是處於暫停狀態,線程被喚醒開始執行;
如果線程本來就處於運行狀態,線程繼續運行,並且會記住這次unpark,線程下次park時無效.
多次調用unpark時僅一次unpark有效.

完美

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章