最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

詳解Java 信號(hào)量Semaphore

 更新時(shí)間:2020年09月14日 11:16:07   作者:java小新人  
這篇文章主要介紹了Java 信號(hào)量Semaphore的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)Java并發(fā),感興趣的朋友可以了解下

  Semaphore也是一個(gè)同步器,和前面兩篇說(shuō)的CountDownLatch和CyclicBarrier不同,這是遞增的,初始化的時(shí)候可以指定一個(gè)值,但是不需要知道需要同步的線程個(gè)數(shù),只需要在同步的地方調(diào)用acquire方法時(shí)指定需要同步的線程個(gè)數(shù);

一.簡(jiǎn)單使用

  同步兩個(gè)子線程,只有其中兩個(gè)子線程執(zhí)行完畢,主線程才會(huì)執(zhí)行:

package com.example.demo.study;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class Study0217 {
  //創(chuàng)建一個(gè)信號(hào)量的實(shí)例,信號(hào)量初始值為0
  static Semaphore semaphore = new Semaphore(0);
  
  public static void main(String[] args) throws Exception {
    ExecutorService pool = Executors.newFixedThreadPool(3);
    pool.submit(()->{
      System.out.println("Thread1---start");
      //信號(hào)量加一
      semaphore.release();
    });
    
    pool.submit(()->{
      System.out.println("Thread2---start");
      //信號(hào)量加一
      semaphore.release();
    });
    pool.submit(()->{
      System.out.println("Thread3---start");
      //信號(hào)量加一
      semaphore.release();
    });
    //等待兩個(gè)子線程執(zhí)行完畢就放過(guò),必須要信號(hào)量等于2才放過(guò)
    semaphore.acquire(2);
    System.out.println("兩個(gè)子線程執(zhí)行完畢");
    
    //關(guān)閉線程池,正在執(zhí)行的任務(wù)繼續(xù)執(zhí)行
    pool.shutdown();

  }

}

這個(gè)信號(hào)量也可以復(fù)用,類似CyclicBarrier:

package com.example.demo.study;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class Study0217 {
  //創(chuàng)建一個(gè)信號(hào)量的實(shí)例,信號(hào)量初始值為0
  static Semaphore semaphore = new Semaphore(0);
  
  public static void main(String[] args) throws Exception {
    ExecutorService pool = Executors.newFixedThreadPool(3);
    pool.submit(()->{
      System.out.println("Thread1---start");
      //信號(hào)量加一
      semaphore.release();
    });
    
    pool.submit(()->{
      System.out.println("Thread2---start");
      //信號(hào)量加一
      semaphore.release();
    });
    
    //等待兩個(gè)子線程執(zhí)行完畢就放過(guò),必須要信號(hào)量等于2才放過(guò)
    semaphore.acquire(2);
    System.out.println("子線程1,2執(zhí)行完畢");
    
    pool.submit(()->{
      System.out.println("Thread3---start");
      //信號(hào)量加一
      semaphore.release();
    });
    pool.submit(()->{
      System.out.println("Thread4---start");
      //信號(hào)量加一
      semaphore.release();
    });
    
    semaphore.acquire(2);
    System.out.println("子線程3,4執(zhí)行完畢");
    
    //關(guān)閉線程池,正在執(zhí)行的任務(wù)繼續(xù)執(zhí)行
    pool.shutdown();

  }

}

二.信號(hào)量原理 

  看看下面這個(gè)圖,可以知道信號(hào)量Semaphore還是根據(jù)AQS實(shí)現(xiàn)的,內(nèi)部有個(gè)Sync工具類操作AQS,還分為公平策略和非公平策略;

構(gòu)造器:

//默認(rèn)是非公平策略
public Semaphore(int permits) {
  sync = new NonfairSync(permits);
}
//可以根據(jù)第二個(gè)參數(shù)選擇是公平策略還是非公平策略
public Semaphore(int permits, boolean fair) {
  sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

acquire(int permits)方法:

public void acquire(int permits) throws InterruptedException {
  if (permits < 0) throw new IllegalArgumentException();
  sync.acquireSharedInterruptibly(permits);
}

//AQS中的方法
public final void acquireSharedInterruptibly(int arg)
    throws InterruptedException {
  if (Thread.interrupted()) throw new InterruptedException();
  //這里根據(jù)子類是公平策略還是非公平策略
  if (tryAcquireShared(arg) < 0)
    //獲取失敗會(huì)進(jìn)入這里,將線程放入阻塞隊(duì)列,然后再嘗試,還是失敗的話就調(diào)用park方法掛起當(dāng)前線程
    doAcquireSharedInterruptibly(arg);
}
//非公平策略
protected int tryAcquireShared(int acquires) {
  return nonfairTryAcquireShared(acquires);
}
final int nonfairTryAcquireShared(int acquires) {
  //一個(gè)無(wú)限循環(huán),獲取state剩余的信號(hào)量,因?yàn)槊空{(diào)用一次release()方法的話,信號(hào)量就會(huì)加一,這里將
  //最新的信號(hào)量減去傳進(jìn)來(lái)的參數(shù)比較,比如有兩個(gè)線程,其中一個(gè)線程已經(jīng)調(diào)用了release方法,然后調(diào)用acquire(2)方法,那么
  //這里remaining的值就是-1,返回-1,然后當(dāng)前線程就會(huì)被丟到阻塞隊(duì)列中去了;如果另外一個(gè)線程也調(diào)用了release方法,
  //那么此時(shí)的remaining==0,所以在這里的if中會(huì)調(diào)用CAS將0設(shè)置到state
  //
  for (;;) {
    int available = getState();
    int remaining = available - acquires;
    if (remaining < 0 || compareAndSetState(available, remaining))
      return remaining;
  }
}
//公平策略
//和上面非公平差不多,只不過(guò)這里會(huì)查看阻塞隊(duì)列中當(dāng)前節(jié)點(diǎn)前面有沒(méi)有前驅(qū)節(jié)點(diǎn),有的話直接返回-1,
//就會(huì)把當(dāng)前線程丟到阻塞隊(duì)列中阻塞去了,沒(méi)有前驅(qū)節(jié)點(diǎn)的話,就跟非公平模式一樣的了
protected int tryAcquireShared(int acquires) {
  for (;;) {
    if (hasQueuedPredecessors())
      return -1;
    int available = getState();
    int remaining = available - acquires;
    if (remaining < 0 ||compareAndSetState(available, remaining))
      return remaining;
  }
}

再看看release(int permits)方法:

//這個(gè)方法的作用就是將信號(hào)量加一
public void release(int permits) {
  if (permits < 0) throw new IllegalArgumentException();
  sync.releaseShared(permits);
}
//AQS中方法
public final boolean releaseShared(int arg) {
  //tryReleaseShared嘗試釋放資源
  if (tryReleaseShared(arg)) {
    //釋放資源成功就調(diào)用park方法喚醒喚醒AQS隊(duì)列中最前面的節(jié)點(diǎn)中的線程
    doReleaseShared();
    return true;
  }
  return false;
}

protected final boolean tryReleaseShared(int releases) {
  //一個(gè)無(wú)限循環(huán),獲取state,然后加上傳進(jìn)去的參數(shù),如果新的state的值小于舊的state,說(shuō)明已經(jīng)超過(guò)了state的最大值,溢出了
  //沒(méi)有溢出的話,就用CAS更新state的值
  for (;;) {
    int current = getState();
    int next = current + releases;
    if (next < current) // overflow
      throw new Error("Maximum permit count exceeded");
    if (compareAndSetState(current, next))
      return true;
  }
}

private void doReleaseShared() {
  
  for (;;) {
    Node h = head;
    if (h != null && h != tail) {
      int ws = h.waitStatus;
      //ws==Node.SIGNAL表示節(jié)點(diǎn)中線程需要被喚醒
      if (ws == Node.SIGNAL) {
        if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
          continue;      // loop to recheck cases
        //調(diào)用阻塞隊(duì)列中線程的unpark方法喚醒線程
        unparkSuccessor(h);
      }
      //ws == 0表示節(jié)點(diǎn)中線程是初始狀態(tài)
      else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
        continue;        // loop on failed CAS
    }
    
    if (h == head)          // loop if head changed
      break;
  }
}

  以最上面的例子簡(jiǎn)單說(shuō)一下,其實(shí)不是很難,首先線程1和線程2分別去調(diào)用release方法,這個(gè)方法里面會(huì)將AQS中的state加一,但是在執(zhí)行這個(gè)操作之前,主線程肯定會(huì)先到acquire(2),在這個(gè)方法里面,假如默認(rèn)使用非公平策略,首先獲取當(dāng)前的信號(hào)量state(state的初始值是0),用當(dāng)前信號(hào)量減去2,如果小于0,那么當(dāng)前主線程就會(huì)丟到AQS隊(duì)列中阻塞;

  這個(gè)時(shí)候線程1的release方法執(zhí)行了,于是就把信號(hào)量state加一(此時(shí)state==1),CAS更新state為一,成功的話,就調(diào)用doReleaseShared()方法喚醒AQS阻塞隊(duì)列中最先掛起的線程(這里就是因?yàn)檎{(diào)用acquire方法而阻塞的主線程),主線程喚醒之后又會(huì)去獲取最新的信號(hào)量,與2比較,發(fā)現(xiàn)還是小于0,于是又會(huì)阻塞;

  線程2此時(shí)的release方法執(zhí)行完成,重復(fù)線程一的操作,主線程喚醒之后(此時(shí)state==2),又去獲取最新的信號(hào)量發(fā)現(xiàn)是2,減去acquire方法的參數(shù)2等于0,于是就用CAS更新state的值,然后acquire方法也就執(zhí)行完畢,主線程繼續(xù)執(zhí)行后面的代碼;

  其實(shí)信號(hào)量還是很有意思的,記得在項(xiàng)目里,有人利用信號(hào)量實(shí)現(xiàn)了一個(gè)故障隔離,什么時(shí)候我可以把整理之后的代碼貼出來(lái)分享一下,還是很有意思的,就跟springcloud的熔斷機(jī)制差不多,場(chǎng)景是:比如你在service的一個(gè)方法調(diào)用第三方的接口,你不知道調(diào)不調(diào)得通,而且你不希望每次前端過(guò)來(lái)都會(huì)去調(diào)用,比如當(dāng)調(diào)用失敗的次數(shù)超過(guò)100次,那么五分鐘之后才會(huì)再去實(shí)際調(diào)用這個(gè)第三方服務(wù)!這五分鐘內(nèi)前調(diào)用這個(gè)服務(wù),就會(huì)觸發(fā)我們這個(gè)故障隔離的機(jī)制,向前端返回一個(gè)特定的錯(cuò)誤碼和錯(cuò)誤信息!

以上就是詳解Java 信號(hào)量Semaphore的詳細(xì)內(nèi)容,更多關(guān)于Java 信號(hào)量Semaphore的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 解決Java項(xiàng)目中request流只能獲取一次的問(wèn)題

    解決Java項(xiàng)目中request流只能獲取一次的問(wèn)題

    Java項(xiàng)目開(kāi)發(fā)中可能存在以下幾種情況,你需要在攔截器中統(tǒng)一攔截請(qǐng)求和你項(xiàng)目里可能需要搞一個(gè)統(tǒng)一的異常處理器,這兩種情況是比較常見(jiàn)的,本文將給大家介紹如何解決Java項(xiàng)目中request流只能獲取一次的問(wèn)題,需要的朋友可以參考下
    2024-02-02
  • 如何使用java給局域網(wǎng)的電腦發(fā)送開(kāi)機(jī)數(shù)據(jù)包

    如何使用java給局域網(wǎng)的電腦發(fā)送開(kāi)機(jī)數(shù)據(jù)包

    這篇文章主要為大家詳細(xì)介紹了如何使用java給局域網(wǎng)的電腦發(fā)送開(kāi)機(jī)數(shù)據(jù)包,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-09-09
  • 看過(guò)就懂的java零拷貝及實(shí)現(xiàn)方式詳解

    看過(guò)就懂的java零拷貝及實(shí)現(xiàn)方式詳解

    這篇文章主要為大家詳細(xì)的介紹了什么是零拷貝,傳統(tǒng)的IO執(zhí)行流程,零拷貝相關(guān)的知識(shí)點(diǎn)回顧,零拷貝實(shí)現(xiàn)的幾種方式及java提供的零拷貝方式相關(guān)內(nèi)容,有需要的朋友可以借鑒參考下
    2022-01-01
  • Java中實(shí)體與Map之間的相互轉(zhuǎn)換代碼示例

    Java中實(shí)體與Map之間的相互轉(zhuǎn)換代碼示例

    生活中經(jīng)常用到map數(shù)據(jù)與實(shí)體類的轉(zhuǎn)換,下面這篇文章主要給大家介紹了關(guān)于Java中實(shí)體與Map之間相互轉(zhuǎn)換的相關(guān)資料,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • 在Java中基于Geotools對(duì)PostGIS數(shù)據(jù)庫(kù)的空間查詢實(shí)踐教程

    在Java中基于Geotools對(duì)PostGIS數(shù)據(jù)庫(kù)的空間查詢實(shí)踐教程

    本文將深入探討這一實(shí)踐,從連接配置到復(fù)雜空間查詢操作,包括點(diǎn)查詢、區(qū)域范圍查詢以及空間關(guān)系判斷等,全方位展示如何在 Java 環(huán)境下借助 Geotools 駕馭 PostGIS 數(shù)據(jù)庫(kù),實(shí)現(xiàn)高效精準(zhǔn)的空間數(shù)據(jù)檢索,為相關(guān)領(lǐng)域開(kāi)發(fā)者提供實(shí)用的技術(shù)路徑,助力空間數(shù)據(jù)應(yīng)用的創(chuàng)新拓展
    2025-05-05
  • Eclipse+Maven構(gòu)建Hadoop項(xiàng)目的方法步驟

    Eclipse+Maven構(gòu)建Hadoop項(xiàng)目的方法步驟

    這篇文章主要介紹了Eclipse+Maven構(gòu)建Hadoop項(xiàng)目的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-02-02
  • JDK21無(wú)法導(dǎo)入TimeUnit類的解決辦法

    JDK21無(wú)法導(dǎo)入TimeUnit類的解決辦法

    這篇文章主要給大家介紹了關(guān)于JDK21無(wú)法導(dǎo)入TimeUnit類的解決辦法,TimeUnit是java.util.concurrent包下面的一個(gè)類,TimeUnit提供了可讀性更好的線程暫停操作,通常用來(lái)替換Thread.sleep(),需要的朋友可以參考下
    2024-01-01
  • SpringBoot快速通關(guān)自動(dòng)配置應(yīng)用

    SpringBoot快速通關(guān)自動(dòng)配置應(yīng)用

    在進(jìn)行項(xiàng)目編寫(xiě)前,我們還需要知道一個(gè)東西,就是SpringBoot對(duì)我們的SpringMVC還做了哪些配置,包括如何擴(kuò)展,如何定制,只有把這些都搞清楚了,我們?cè)谥笫褂貌艜?huì)更加得心應(yīng)手
    2022-07-07
  • 一次線上websocket返回400問(wèn)題排查的實(shí)戰(zhàn)記錄

    一次線上websocket返回400問(wèn)題排查的實(shí)戰(zhàn)記錄

    最近項(xiàng)目中有端對(duì)端通信場(chǎng)景,實(shí)時(shí)性要求較高,考慮后選用了websocket 這一通信協(xié)議,下面這篇文章主要給大家介紹了一次線上websocket返回400問(wèn)題排查的實(shí)戰(zhàn)記錄,需要的朋友可以參考下
    2022-04-04
  • 關(guān)于java.lang.IllegalArgumentException異常的正確解決方法

    關(guān)于java.lang.IllegalArgumentException異常的正確解決方法

    java.lang.IllegalArgumentException 是 Java 編程語(yǔ)言中的一個(gè)運(yùn)行時(shí)異常,通常表示向方法傳遞了一個(gè)不合法或不適當(dāng)?shù)膮?shù),本文給大家分享有關(guān)java.lang.IllegalArgumentException異常的正確解決方法,感興趣的朋友跟隨小編一起看看吧
    2025-09-09

最新評(píng)論

安顺市| 淮滨县| 开江县| 钦州市| 栾城县| 远安县| 黔东| 武冈市| 云浮市| 延川县| 普洱| 武义县| 陆川县| 洪江市| 济阳县| 滨海县| 时尚| 台湾省| 离岛区| 当雄县| 长葛市| 广州市| 嘉荫县| 宁晋县| 浪卡子县| 芮城县| 堆龙德庆县| 安顺市| 普兰店市| 辽宁省| 桃园县| 泌阳县| 宝坻区| 大英县| 五大连池市| 靖安县| 房产| 武夷山市| 全椒县| 安福县| 洞口县|