<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          LockSupport 線程工具類有啥用?

          共 10250字,需瀏覽 21分鐘

           ·

          2021-06-23 22:54

          點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)

          本文轉(zhuǎn)自公眾號(hào):程序猿阿星

          內(nèi)容大綱

          LockSupport基本概念

          LockSupport是線程工具類,主要作用是阻塞和喚醒線程,底層實(shí)現(xiàn)依賴Unsafe,同時(shí)它還是鎖和其他同步類實(shí)現(xiàn)的基礎(chǔ),LockSupport提供兩類靜態(tài)函數(shù)分別是parkunpark,即阻塞與喚醒線程,下面是兩段代碼示例
          示例-1
          public static void main(String[] agrs) throws InterruptedException {
                  Thread th = new Thread(() -> {
                      //阻塞當(dāng)前線程
                      LockSupport.park();
                      System.out.println("子線程執(zhí)行---------");
                  });
                  th.start();
                  //睡眠2秒
                  Thread.sleep(2000);
                  System.out.println("主線程執(zhí)行---------");
                  //喚醒線程
                  LockSupport.unpark(th);
              }
          }


          輸出結(jié)果:
          主線程執(zhí)行---------
          子線程執(zhí)行---------

          上述示例中,子線程th調(diào)用LockSupport.park()阻塞,主線程睡眠2秒后,執(zhí)行LockSupport.unpark(th)喚醒th線程,先阻塞后喚醒非常好理解,接下來讀者們?cè)倏聪旅娴氖纠?/section>
          示例-2
          public static void main(String[] agrs) throws InterruptedException {
                  Thread th = new Thread(() -> {
                      //喚醒當(dāng)前線程
                      LockSupport.unpark(Thread.currentThread());
                      //阻塞當(dāng)前線程
                      LockSupport.park();
                      System.out.println("子線程執(zhí)行---------");
                  });
                  th.start();
                  //睡眠2秒
                  Thread.sleep(2000);
                  System.out.println("主線程執(zhí)行---------");
          }



          輸出結(jié)果:
          子線程執(zhí)行---------
          主線程執(zhí)行---------

          嗯?先喚醒th線程,再阻塞th線程,最終th線程沒有被阻塞,這是為什么?下面LockSupport的設(shè)計(jì)思路會(huì)為讀者們解開疑惑,并更進(jìn)一步明確是parkunpark的語義(從廣義上來說parkunpark代表阻塞和喚醒)。

          設(shè)計(jì)思路

          LockSupport的設(shè)計(jì)思路是通過許可證來實(shí)現(xiàn)的,就像汽車上高速公路,入口處要獲取通行卡,出口處要交出通行卡,如果沒有通行卡你就無法出站,當(dāng)然你可以選擇補(bǔ)一張通行卡。
          LockSupport會(huì)為使用它的線程關(guān)聯(lián)一個(gè)許可證(permit)狀態(tài),permit的語義「是否擁有許可」,0代表否,1代表是,默認(rèn)是0。
          • LockSupport.unpark:指定線程關(guān)聯(lián)的permit直接更新為1,如果更新前的permit<1,喚醒指定線程
          • LockSupport.park:當(dāng)前線程關(guān)聯(lián)的permit如果>0,直接把permit更新為0,否則阻塞當(dāng)前線程
          • 線程A執(zhí)行LockSupport.park,發(fā)現(xiàn)permit0,未持有許可證,阻塞線程A
          • 線程B執(zhí)行LockSupport.unpark(入?yún)⒕€程A),為A線程設(shè)置許可證,permit更新為1,喚醒線程A
          • 線程B流程結(jié)束
          • 線程A被喚醒,發(fā)現(xiàn)permit1,消費(fèi)許可證,permit更新為0
          • 線程A執(zhí)行臨界區(qū)
          • 線程A流程結(jié)束
          經(jīng)過上面的分析得出結(jié)論unpark的語義明確為「使線程持有許可證」,park的語義明確為「消費(fèi)線程持有的許可」,所以unparkpark的執(zhí)行順序沒有強(qiáng)制要求,只要控制好使用的線程即可,unpark=>park執(zhí)行流程如下
          • permit默認(rèn)是0,線程A執(zhí)行LockSupport.unpark,permit更新為1,線程A持有許可證
          • 線程A執(zhí)行LockSupport.park,此時(shí)permit1,消費(fèi)許可證,permit更新為0
          • 執(zhí)行臨界區(qū)
          • 流程結(jié)束
          最后再補(bǔ)充下park注意點(diǎn),因park阻塞的線程不僅僅會(huì)被unpark喚醒,還可能會(huì)被線程中斷(Thread.interrupt)喚醒,而且不會(huì)拋出InterruptedException異常,所以建議在park后自行判斷線程中斷狀態(tài),來做對(duì)應(yīng)的業(yè)務(wù)處理。

          優(yōu)點(diǎn)

          為什么推薦使用LockSupport來做線程的阻塞與喚醒(線程間協(xié)同工作),因?yàn)樗邆淙缦聝?yōu)點(diǎn)
          • 以線程為操作對(duì)象更符合阻塞線程的直觀語義
          • 操作更精準(zhǔn),可以準(zhǔn)確地喚醒某一個(gè)線程(notify隨機(jī)喚醒一個(gè)線程,notifyAll喚醒所有等待的線程)
          • 無需競爭鎖對(duì)象(以線程作為操作對(duì)象),不會(huì)因競爭鎖對(duì)象產(chǎn)生死鎖問題
          • unparkpark沒有嚴(yán)格的執(zhí)行順序,不會(huì)因執(zhí)行順序引起死鎖問題,比如「Thread.suspendThread.resume」沒按照嚴(yán)格順序執(zhí)行,就會(huì)產(chǎn)生死鎖
          另外LockSupport還提供了park的重載函數(shù),提升靈活性
          • void parkNanos(long nanos):增加了超時(shí)機(jī)制
          • void parkUntil(long deadline):加入超時(shí)機(jī)制(指定到某個(gè)時(shí)間點(diǎn),1970年到指定時(shí)間點(diǎn)的毫秒數(shù))
          • void park(Object blocker):設(shè)置blocker對(duì)象,當(dāng)線程沒有許可證被阻塞時(shí),該對(duì)象會(huì)被記錄到該線程的內(nèi)部,方便后續(xù)使用診斷工具進(jìn)行問題排查
          • void parkNanos(Object blocker, long nanos):設(shè)置blocker對(duì)象,加入超時(shí)機(jī)制
          • void parkUntil(Object blocker, long deadline):設(shè)置blocker對(duì)象,加入超時(shí)機(jī)制(指定到某個(gè)時(shí)間點(diǎn),1970年到指定時(shí)間點(diǎn)的毫秒數(shù))
          建議使用時(shí),傳入blocker對(duì)象,至于超時(shí)根據(jù)業(yè)務(wù)場景選擇

          實(shí)踐

          使用LockSupport來完成一道阿里經(jīng)典的多線程協(xié)同工作面試題。
          3個(gè)獨(dú)立的線程,一個(gè)只會(huì)輸出A,一個(gè)只會(huì)輸出B,一個(gè)只會(huì)輸出C,在三個(gè)線程啟動(dòng)的情況下,請(qǐng)用合理的方式讓他們按順序打印ABCABC。
          思路如下
          • 準(zhǔn)備3個(gè)線程,分別固定打印A、B、C
          • 線程輸出完A、B、C后需要阻塞等待喚醒
          • 額外準(zhǔn)備第4個(gè)線程,作為另外3個(gè)線程的調(diào)度器,有序的控制3個(gè)線程執(zhí)行
          是不是很簡單,下面通過代碼來實(shí)踐
          public static void main(String[] agrs) throws InterruptedException {

                  LockSupportMain lockSupportMain = new LockSupportMain();
                  
                  //定義線程t1、t2、t3執(zhí)行的函數(shù)方法
                  Consumer<String> consumer = str -> {
                      while (true) {
                          //線程消費(fèi)許可證,并傳入blocker,方便后續(xù)排查問題
                          LockSupport.park(lockSupportMain);
                          //防止線程是因中斷操作喚醒
                          if (Thread.currentThread().isInterrupted()){
                              throw new RuntimeException("線程被中斷,異常結(jié)束");
                          }
                          System.out.println(Thread.currentThread().getName() + ":" + str);
                      }
                  };
                  
                  /**
                   * 定義分別輸出A、B、C的線程
                   */

                  Thread t1 = new Thread(() -> {
                      consumer.accept("A");
                  },"T1");
                  Thread t2 = new Thread(() -> {
                      consumer.accept("B");
                  },"T2");
                  Thread t3 = new Thread(() -> {
                      consumer.accept("C");
                  },"T3");

                  
                  /**
                   * 定義調(diào)度線程
                   */

                  Thread dispatch = new Thread(() -> {
                      int i=0;
                      try {
                          while (true) {
                              if((i%3)==0) {
                                  //線程t1設(shè)置許可證,并喚醒線程t1
                                  LockSupport.unpark(t1);
                              }else if((i%3)==1) {
                                  //線程t2設(shè)置許可證,并喚醒線程t2
                                  LockSupport.unpark(t2);
                              }else {
                                  //線程t3設(shè)置許可證,并喚醒線程t3
                                  LockSupport.unpark(t3);
                              }
                              i++;
                              TimeUnit.MILLISECONDS.sleep(500);
                          }
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  });

                  //啟動(dòng)相關(guān)線程
                  t1.start();
                  t2.start();
                  t3.start();
                  dispatch.start();
                  
          }


          輸出內(nèi)容:
          T1:A
          T2:B
          T3:C
          T1:A
          T2:B
          T3:C
          T1:A
          T2:B
          T3:C
          最后再留個(gè)題目給讀者們思考,使用包含但不限于SynchronizedReentrantLock來完成這個(gè)功能。另外,關(guān)注公眾號(hào)Java技術(shù)棧,在后臺(tái)回復(fù):面試,可以獲取我整理的 Java 多線程系列面試題和答案,非常齊全。






          關(guān)注Java技術(shù)??锤喔韶?/strong>



          獲取 Spring Boot 實(shí)戰(zhàn)筆記!
          瀏覽 53
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  黄色成人视频在线免费看 | 免费看靠逼视频 | 国产视屏123区 | 国产乱伦免费 | 超碰人人操国产 |