溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

LockSupport的原理和作用是什么

發(fā)布時間:2021-06-28 16:21:22 來源:億速云 閱讀:140 作者:chen 欄目:大數(shù)據(jù)

本篇內(nèi)容介紹了“LockSupport的原理和作用是什么”的有關(guān)知識,在實際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

LockSupport是什么

用于創(chuàng)建鎖和其他同步類的基本線程阻塞原語。
使用它,可以用來構(gòu)建一些特定邏輯的鎖。jdk并發(fā)包的AQS框架就依賴了它。

LockSupport有什么作用

它提供了線程的阻塞和喚醒。
每當(dāng)一個線程使用它,那么就可以當(dāng)作這個線程維持了一個二元信號量,可以比作持有許可證。
調(diào)用pack()方法,如果有許可證,那么消耗它并且直接返回,線程繼續(xù)執(zhí)行下去。反之,則阻塞。
調(diào)用unpack()方法,如果許可證不可用,那么就變成可用,如果是可用,那么就不做其他操作。也就是說,許可證不可以多次累加。
因為有許可證這種中間介質(zhì),也就避免了調(diào)用線程不推薦的Thread.suspend()Thread.resume()而導(dǎo)致的線程失活的竟態(tài)情況。這里我個人的理解是,線程調(diào)度的復(fù)雜性和延遲。比如說:線程的休眠和暫停并不能夠做到即可馬上、命令之間的重排等等。并且,park()也額外支持了線程打斷和超時功能。
類的文檔里面額外提及了一個慣用法,說的是pack()調(diào)用會"無理由"返回,從而導(dǎo)致線程莫名的執(zhí)行下去。那么慣用法就是把pack()邏輯包裝在一個循環(huán)當(dāng)中,并且循環(huán)退出的條件就是線程等待同步許可的條件。這就相當(dāng)于一個自旋的優(yōu)化,只是需要unpack()配合。

一個阻塞線程的簡單范例:

public static void main(String[] args) {
    LockSupport.park();
    System.out.println("主線程阻塞,并不會打印當(dāng)前消息");
}

一個通用的使用范式:

    public static void main(String[] args) throws InterruptedException {
        Thread busThreadJim = new Thread(() -> {

            System.out.println("參數(shù)檢查...");
            System.out.println("執(zhí)行業(yè)務(wù)邏輯...");
            System.out.println("進(jìn)入狀態(tài)同步點");
            LockSupport.park("因為可能原因A而阻塞住線程");

            System.out.println("同步點完成,進(jìn)步業(yè)務(wù)收尾階段...");
            System.out.println("進(jìn)入最后一個同步點結(jié)束");
            LockSupport.park("報告這個線程當(dāng)前的工作程度或者狀態(tài)xxxx");

            System.out.println("業(yè)務(wù)邏輯完成退出");
        });
        busThreadJim.start();

        System.out.println("控制線程或者監(jiān)控線程開始做其他邏輯...");
        Thread.sleep(2000);
        System.out.println("檢查工作線程是否阻塞住,為什么:" + LockSupport.getBlocker(busThreadJim));
        LockSupport.unpark(busThreadJim);

        // 如果線程調(diào)度慢 LockSupport.getBlocker獲取到null
        // 也就是說工作線程還沒有阻塞住
        System.out.println("可以通過阻塞對象來傳出一些線程工作信息:" + LockSupport.getBlocker(busThreadJim));
        System.out.println("當(dāng)然共享變量也是可以的");
        LockSupport.unpark(busThreadJim);

        System.out.println("邏輯完成");
    }

多次unpack()也只能消耗一次

    public static void main(String[] args) throws InterruptedException {

        Thread worker = new Thread(() -> {

            System.out.println("執(zhí)行業(yè)務(wù)邏輯...");
            System.out.println("進(jìn)入狀態(tài)同步點...");

            LockSupport.park();

            System.out.println("同步點完成,進(jìn)步業(yè)務(wù)收尾階段...");

            LockSupport.park();

            System.out.println("最后一個同步點結(jié)束");
        });

        worker.start();
        LockSupport.unpark(worker);
        LockSupport.unpark(worker);

        Thread.sleep(2000);

        LockSupport.unpark(worker);
    }

設(shè)計代碼邏輯的時候,盡量有明確的阻塞條件,然后選擇帶有時間截至的函數(shù)和提供阻塞信息的對象。
理論上來說,應(yīng)該用不到這個類。但是一旦用到了,估計就是設(shè)計很基礎(chǔ)的東西,上面肯定有復(fù)雜的邏輯。沒有阻塞信息到時候邏輯排查是要命的。

與wait/notify機(jī)制的區(qū)別

  1. wait/notify必須在同步代碼塊中使用,光這一點就感覺到限制比較大。

  2. wait/notify操作的是對象,然后才能映射到對象所在的線程,思考方式有點饒。

  3. 復(fù)雜的邏輯,特別是嵌套,特別窒息。

源碼實現(xiàn)關(guān)鍵點

public class LockSupport {
    private LockSupport() {} // 該類無法實例化

    // 所有的功能都代理給內(nèi)部來實現(xiàn)
    private static final sun.misc.Unsafe UNSAFE;
    
    // 把當(dāng)前線程阻塞住
    public static void park() {
        UNSAFE.park(false, 0L);
    }

    // 把當(dāng)前執(zhí)行線程與一個阻塞對象相關(guān)連
    // 這樣子關(guān)注對象就變成線程而非對象
    public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }
    
    // 下面就是一系列時間相關(guān)函數(shù)
    public static void parkNanos(Object blocker, long nanos) {
        if (nanos > 0) {
            Thread t = Thread.currentThread();
            setBlocker(t, blocker);
            UNSAFE.park(false, nanos);
            setBlocker(t, null);
        }
    }

    public static void parkUntil(Object blocker, long deadline) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(true, deadline);
        setBlocker(t, null);
    }

    public static void parkNanos(long nanos) {
        if (nanos > 0)
            UNSAFE.park(false, nanos);
    }

    public static void parkUntil(long deadline) {
        UNSAFE.park(true, deadline);
    }

    // 喚醒目標(biāo)也是線程 跟阻塞關(guān)注點相同
    public static void unpark(Thread thread) {
        if (thread != null)
            UNSAFE.unpark(thread);
    }

    // 返回阻塞對象信息
    // 如果頻繁的pack,那么該對象可能一直在變更,它的生命周期會比較短,只是最近一次阻塞的信息
    public static Object getBlocker(Thread t) {
        if (t == null)
            throw new NullPointerException();
        return UNSAFE.getObjectVolatile(t, parkBlockerOffset);
    }
}

值得注意的關(guān)注點在于blocker的信息獲取,直接操作內(nèi)存,因為線程已經(jīng)阻塞住,只能通過這種機(jī)制來獲取阻塞線程內(nèi)信息。

“LockSupport的原理和作用是什么”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI