日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

Java Review - 并发编程_LockSupport

發(fā)布時間:2025/3/21 java 17 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java Review - 并发编程_LockSupport 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

文章目錄

  • 概述
  • 主要方法
    • void park()
    • void unpark(Thread thread)
    • 小示例 park() & unpark(Thread thread)
    • void parkNanos(long nanos)
    • park(Object blocker)
    • void parkNanos(Object blocker, long nanos)
    • void parkUntil(Object blocker, long deadline)
  • 示例


概述

位于rt.jar包的java.util.concurrent.locks目錄中, 主要作用是掛起和喚醒線程,該工具類是創(chuàng)建鎖和其他同步類的基礎(chǔ)。

LockSupport類與每個使用它的線程都會關(guān)聯(lián)一個許可證,在默認情況下調(diào)用LockSupport類的方法的線程是不持有許可證的。


主要方法

void park()

如果調(diào)用park方法的線程已經(jīng)拿到了與LockSupport關(guān)聯(lián)的許可證,則調(diào)用LockSupport.park()時會馬上返回,否則調(diào)用線程會被禁止參與線程的調(diào)度,也就是會被阻塞掛起。

public static void main(String[] args) {System.out.println("begin park");LockSupport.park();System.out.println("end park");}

直接在main函數(shù)里面調(diào)用park方法,最終只會輸出begin park!,然后當(dāng)前線程被掛起,這是因為在默認情況下調(diào)用線程是不持有許可證的。

在其他線程調(diào)用 unpark(Thread thread)方法并且將當(dāng)前線程作為參數(shù)時,調(diào)用park方法而被阻塞的線程會返回。

另外,如果其他線程調(diào)用了阻塞線程的interrupt()方法,設(shè)置了中斷標志或者線程被虛假喚醒,則阻塞線程也會返回。

  • 所以在調(diào)用park方法時最好也使用循環(huán)條件判斷方式。

  • 需要注意的是,因調(diào)用park()方法而被阻塞的線程被其他線程中斷而返回時并不會拋出InterruptedException異常。


void unpark(Thread thread)

當(dāng)一個線程調(diào)用unpark時,如果參數(shù)thread線程沒有持有thread與LockSupport類關(guān)聯(lián)的許可證,則讓thread線程持有。

如果thread之前因調(diào)用park()而被掛起,則調(diào)用unpark后,該線程會被喚醒如果thread之前沒有調(diào)用park,則調(diào)用unpark方法后,再調(diào)用park方法,其會立刻返回。修改代碼如下。

public static void main(String[] args) {System.out.println("begin park");LockSupport.unpark(Thread.currentThread());LockSupport.park();System.out.println("end park");}


小示例 park() & unpark(Thread thread)

public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{System.out.println("child thread begin to park");LockSupport.park();System.out.println("child thread unpark");});// 啟動子線程thread.start();// 主線程休眠1sTimeUnit.SECONDS.sleep(1);// 調(diào)用unpark方法讓thread持有許可證,然后park方法返回System.out.println("main thread begin unpark ");LockSupport.unpark(thread);}

  • 首先創(chuàng)建了一個子線程thread,子線程啟動后調(diào)用park方法,由于在默認情況下子線程沒有持有許可證,因而它會把自己掛起

  • 主線程休眠1s是為了讓主線程調(diào)用unpark方法前讓子線程輸出child thread begin park!并阻塞

  • 主線程然后執(zhí)行unpark方法,參數(shù)為子線程,這樣做的目的是讓子線程持有許可證,然后子線程調(diào)用的park方法就返回了

park方法返回時不會告訴你因何種原因返回,所以調(diào)用者需要根據(jù)之前調(diào)用park方法的原因,再次檢查條件是否滿足,如果不滿足則還需要再次調(diào)用park方法

例如,根據(jù)調(diào)用前后中斷狀態(tài)的對比就可以判斷是不是因為被中斷才返回的。

為了說明調(diào)用park方法后的線程被中斷后會返回,我們修改上面的例子代碼,刪除LockSupport.unpark(thread);,然后添加thread.interrupt();,

import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/12/5 8:53* @mark: show me the code , change the world*/ public class LockSupportDemo {public static void main(String[] args) throws InterruptedException {Thread thread = new Thread(()->{System.out.println("child thread begin to park");// 調(diào)用park方法,掛起自己,只有被中斷才推出循環(huán)while(!Thread.currentThread().isInterrupted()){LockSupport.park();}System.out.println("child thread unpark");});// 啟動子線程thread.start();// 主線程休眠1sTimeUnit.SECONDS.sleep(1);// 中斷子線程thread.interrupt();} }

在如上代碼中,只有中斷子線程,子線程才會運行結(jié)束,如果子線程不被中斷,即使你調(diào)用unpark(thread)方法子線程也不會結(jié)束。


void parkNanos(long nanos)

和park方法類似,如果調(diào)用park方法的線程已經(jīng)拿到了與LockSupport關(guān)聯(lián)的許可證,則調(diào)用LockSupport.parkNanos(long nanos)方法后會馬上返回。該方法的不同在于,如果沒有拿到許可證,則調(diào)用線程會被掛起nanos時間后修改為自動返回。

另外park方法還支持帶有blocker參數(shù)的方法void park(Object blocker)方法,當(dāng)線程在沒有持有許可證的情況下調(diào)用park方法而被阻塞掛起時,這個blocker對象會被記錄到該線程內(nèi)部。

使用診斷工具可以觀察線程被阻塞的原因,診斷工具是通過調(diào)用getBlocker(Thread)方法來獲取blocker對象的,所以JDK推薦我們使用帶有blocker參數(shù)的park方法,并且blocker被設(shè)置為this,這樣當(dāng)在打印線程堆棧排查問題時就能知道是哪個類被阻塞了

public class TestPark {public static void main(String[] args) {TestPark testPark = new TestPark();testPark.parkTest();}private void parkTest() {// 調(diào)用park 掛起自己LockSupport.park();} }

運行代碼后,使用jstack pid命令查看線程堆棧時可以看到如下輸出結(jié)果。

使用帶blocker參數(shù)的park方法,線程堆棧可以提供更多有關(guān)阻塞對象的信息。


park(Object blocker)

來看下源碼

public static void park(Object blocker) {// 當(dāng)前線程Thread t = Thread.currentThread();// 設(shè)置線程的blocker變量setBlocker(t, blocker);// 掛起線程UNSAFE.park(false, 0L);// 線程被激活后,清除blocker變量,因為一般都是在線程阻塞的時候才分析原因setBlocker(t, null);}

Thread類里面有個變量volatile Object parkBlocker,用來存放park方法傳遞的blocker對象,也就是把blocker變量存放到了調(diào)用park方法的線程的成員變量里面。


void parkNanos(Object blocker, long nanos)

相比park(Object blocker) 方法多了個超時時間。


void parkUntil(Object blocker, long deadline)

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

其中參數(shù)deadline的時間單位為ms,該時間是從1970年到現(xiàn)在某一個時間點的毫秒值。這個方法和parkNanos(Object blocker, long nanos)方法的區(qū)別是,后者是從當(dāng)前算等待nanos秒時間,而前者是指定一個時間點,比如需要等到2021.12.05日 12:00:00,則把這個時間點轉(zhuǎn)換為從1970年到這個時間點的總毫秒數(shù)。


示例

import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.LockSupport;/*** @author 小工匠* @version 1.0* @description: TODO* @date 2021/12/5 9:42* @mark: show me the code , change the world*/ public class FIFOMutex {private final AtomicBoolean locked = new AtomicBoolean(false);private final Queue<Thread> waiters = new ConcurrentLinkedQueue<Thread>();public void lock(){boolean wasInterrupted = false ;// 當(dāng)期線程Thread current = Thread.currentThread();waiters.add(current);// 1 只有隊首的線程可以獲取鎖while(waiters.peek() != current || !locked.compareAndSet(false,true)){LockSupport.park(this);if (Thread.interrupted()) { // 2wasInterrupted = true;}}waiters.remove();if (wasInterrupted){ // 3current.interrupt();}}public void unlock(){locked.set(false);LockSupport.unpark(waiters.peek());}}

這是一個先進先出的鎖,也就是只有隊列的首元素可以獲取鎖。

  • 在代碼(1)處,如果當(dāng)前線程不是隊首或者當(dāng)前鎖已經(jīng)被其他線程獲取,則調(diào)用park方法掛起自己。

  • 然后在代碼(2)處判斷,如果park方法是因為被中斷而返回,則忽略中斷,并且重置中斷標志,做個標記,然后再次判斷當(dāng)前線程是不是隊首元素或者當(dāng)前鎖是否已經(jīng)被其他線程獲取,如果是則繼續(xù)調(diào)用park方法掛起自己。

  • 然后在代碼(3)中,判斷標記,如果標記為true則中斷該線程,這個怎么理解呢?其實就是其他線程中斷了該線程,雖然我對中斷信號不感興趣,忽略它,但是不代表其他線程對該標志不感興趣,所以要恢復(fù)下。

總結(jié)

以上是生活随笔為你收集整理的Java Review - 并发编程_LockSupport的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。