溫馨提示×

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

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

如何理解LockSupport類(lèi)中的park等待和unpark喚醒

發(fā)布時(shí)間:2021-10-09 16:10:48 來(lái)源:億速云 閱讀:147 作者:iii 欄目:編程語(yǔ)言

這篇文章主要講解了“如何理解LockSupport類(lèi)中的park等待和unpark喚醒”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“如何理解LockSupport類(lèi)中的park等待和unpark喚醒”吧!

一、傳統(tǒng)synchronized隱式鎖

package com.lau.javabase.lock.LockSupport;

import java.util.concurrent.TimeUnit;

/**
 * 使用LockSupport之前,synchronized傳統(tǒng)方式存在的問(wèn)題:
 * 1、wait()和notify()方法不能脫離同步代碼塊(鎖)單獨(dú)使用
 * 2、B線(xiàn)程的notify()方法在A(yíng)線(xiàn)程的wait()之前執(zhí)行的話(huà),A線(xiàn)程將不會(huì)被喚醒
 */
public class BeforeUseTraditional {

    public static void main(String[] args) {
        Object lockObj = new Object();

        //線(xiàn)程A
        new Thread(() -> {
//            try {
//                TimeUnit.SECONDS.sleep(3);
//            } catch (InterruptedException e) {
//                e.printStackTrace();
//            }
//            synchronized (lockObj){
                System.out.println(Thread.currentThread().getName() + " come in...");
                try {
                    lockObj.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " awakened...");
//            }
        }, "A").start();

//        try {
//            TimeUnit.SECONDS.sleep(3);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        //線(xiàn)程B喚醒線(xiàn)程A
        new Thread(() -> {
//            synchronized (lockObj){
                lockObj.notify();
                System.out.println(Thread.currentThread().getName() + " notify...");
//            }
        }, "B").start();
    }

}

輸出:

A come in...
Exception in thread "A" Exception in thread "B" java.lang.IllegalMonitorStateException
	at java.lang.Object.notify(Native Method)
	at com.lau.javabase.lock.LockSupport.BeforeUseTraditional.lambda$main$1(BeforeUseTraditional.java:42)
	at java.lang.Thread.run(Thread.java:745)
java.lang.IllegalMonitorStateException
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at com.lau.javabase.lock.LockSupport.BeforeUseTraditional.lambda$main$0(BeforeUseTraditional.java:25)
	at java.lang.Thread.run(Thread.java:745)

Process finished with exit code 0

結(jié)論:wait()和notify()方法不能脫離同步代碼塊(鎖)單獨(dú)使用

二、ReentrantLock顯示鎖

package com.lau.javabase.lock.LockSupport;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用LockSupport之前,Lock方式存在的問(wèn)題:
 * 1、await()和signal()方法不能脫離同步代碼塊(鎖)單獨(dú)使用
 * 2、B線(xiàn)程的和signal()方法在A(yíng)線(xiàn)程的await()之前執(zhí)行的話(huà),A線(xiàn)程將不會(huì)被喚醒
 */
public class BeforeUseLock {

    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition condition = lock.newCondition();

        //線(xiàn)程A
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

               try {
                    lock.lock();
                   System.out.println(Thread.currentThread().getName() + " come in...");
                   try {
                       condition.await();
                   } catch (Exception e) {
                       e.printStackTrace();
                   }
                   System.out.println(Thread.currentThread().getName() + " awakened...");
               }
               finally {
                   lock.unlock();
               }
        }, "A").start();

//        try {
//            TimeUnit.SECONDS.sleep(3);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        //線(xiàn)程B喚醒線(xiàn)程A
        new Thread(() -> {
            try{
                lock.lock();
                condition.signal();
                System.out.println(Thread.currentThread().getName() + " notify...");
            }
            finally {
                lock.unlock();
            }
        }, "B").start();
    }

}

輸出:

B notify...
A come in...

結(jié)論:B線(xiàn)程的和signal()方法在A(yíng)線(xiàn)程的await()之前執(zhí)行的話(huà),A線(xiàn)程將不會(huì)被喚醒

三、LockSupport類(lèi)

package com.lau.javabase.lock.LockSupport;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

/**
 * 使用LockSupport,不會(huì)存在以下問(wèn)題:
 * 1、await()和signal()方法不能脫離同步代碼塊(鎖)單獨(dú)使用
 * 2、B線(xiàn)程的和signal()方法在A(yíng)線(xiàn)程的await()之前執(zhí)行的話(huà),A線(xiàn)程將不會(huì)被喚醒
 */
public class LockSupportTest {

    public static void main(String[] args) {
        //線(xiàn)程A
        Thread threadA = new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

           System.out.println(Thread.currentThread().getName() + " come in...");

           LockSupport.park();
           System.out.println(Thread.currentThread().getName() + " awakened...");
        }, "A");

        threadA.start();

//        try {
//            TimeUnit.SECONDS.sleep(3);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        }

        //線(xiàn)程B喚醒線(xiàn)程A
        new Thread(() -> {
            LockSupport.unpark(threadA);
            System.out.println(Thread.currentThread().getName() + " notify...");
        }, "B").start();
    }

}

輸出:

B notify...
A come in...
A awakened...

結(jié)論:使用LockSupport,不會(huì)存在以上兩個(gè)問(wèn)題

四、說(shuō)明

LockSupport是用來(lái)創(chuàng)建鎖和其他同步類(lèi)的基本線(xiàn)程阻塞原語(yǔ)
LockSupport是一個(gè)線(xiàn)程阻塞工具類(lèi),所有的方法都是靜態(tài)方法,可以讓線(xiàn)程在任意位置阻塞,阻塞之后也有對(duì)應(yīng)的喚醒方法。歸根
結(jié)底,LockSupport調(diào)用的Unsafe中的native代碼。
 
LockSupport提供park()和unpark()方法實(shí)現(xiàn)阻塞線(xiàn)程和解除線(xiàn)程阻塞的過(guò)程
LockSupport和每個(gè)使用它的線(xiàn)程都有一個(gè)許可(permit)關(guān)聯(lián)。permit相當(dāng)于1,0的開(kāi)關(guān),默認(rèn)是0,
調(diào)用一次unpark就加1變成1,
調(diào)用一次park會(huì)消費(fèi)permit,也就是將1變成o,同時(shí)park立即返回。
如再次調(diào)用park會(huì)變成阻塞(因?yàn)閜ermit為零了會(huì)阻塞在這里,一直到permit變?yōu)?),這時(shí)調(diào)用unpark會(huì)把permit置為1。
每個(gè)線(xiàn)程都有一個(gè)相關(guān)的permit, permit最多只有一個(gè),重復(fù)調(diào)用unpark也不會(huì)積累憑證。
 
形象的理解
線(xiàn)程阻塞需要消耗憑證(permit),這個(gè)憑證最多只有1個(gè)。
當(dāng)調(diào)用park方法時(shí)
    *如果有憑證,則會(huì)直接消耗掉這個(gè)憑證然后正常退出;
    *如果無(wú)憑證,就必須阻塞等待憑證可用;
而unpark則相反,它會(huì)增加一個(gè)憑證,但憑證最多只能有1個(gè),累加無(wú)效。

五、擴(kuò)展

1、為什么可以先喚醒線(xiàn)程后阻塞線(xiàn)程?
因?yàn)閡npark獲得了一個(gè)憑證,之后再調(diào)用park方法,就可以名正言順的憑證消費(fèi),故不會(huì)阻塞。
 
2、為什么喚醒兩次后阻塞兩次,但最終結(jié)果還會(huì)阻塞線(xiàn)程?
因?yàn)閼{證的數(shù)量最多為1,連續(xù)調(diào)用兩次unpark和調(diào)用一次unpark效果一樣,只會(huì)增加一個(gè)憑證;
而調(diào)用兩次park卻需要消費(fèi)兩個(gè)憑證,證不夠,不能放行。

感謝各位的閱讀,以上就是“如何理解LockSupport類(lèi)中的park等待和unpark喚醒”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)如何理解LockSupport類(lèi)中的park等待和unpark喚醒這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

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

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

AI