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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java并发辅助类

發布時間:2023/12/18 java 32 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java并发辅助类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

序言

由于最近項目上遇到了高并發問題,而自己對高并發,多線程這里的知識點相對薄弱,尤其是基礎,所以想系統的學習一下,以后可能會出一系列的JUC文章及總結 ,同時也為企業級的高并發項目做好準備。

本文是JUC文章的第五篇,如想看以往關于JUC文章,請點擊JUC系列總結

此系列文章的總結思路大致分為三部分:

  • 理論(概念);
  • 實踐(代碼證明);
  • 總結(心得及適用場景)。
  • 在這里提前說也是為了防止大家看著看著就迷路了。

    備注:本文的知識深度相對較淺,可能僅局限于應用層面,如您需要相應足夠的深度,請另行查閱。

    ?

    Java并發輔助類大綱

    CountDownLatch

    ?

    什么是CountDownLatch呢?

    CountDownLatch:一個線程(或者多個線程),等待另外N個線程完成某個事情之后,方可執行。

    可能大家看完之后還是云里霧里的,我還是給大家舉一個實際場景的例子把。

    模擬場景:

  • 教室里有7個人上自習,分為為一個班長和其他6位同學;

  • 現在的要求是班長必須最后一個走,因為他是班長,所以一定要確保走后教室燈關掉,門鎖上;

  • 其他6位同學隨時可以走;

    ?

  • 常用方法

    • CountDownLatch(int count); //構造方法,創建一個值為count 的計數器。
    • await();//阻塞當前線程,將當前線程加入阻塞隊列。
    • await(long timeout, TimeUnit unit);//在timeout的時間之內阻塞當前線程,時間一過則當前線程可以執行,
    • countDown();//對計數器進行遞減1操作,當計數器遞減至0時,當前線程會去喚醒阻塞隊列里的所有線程。

    ?

    代碼證明

    public class CountDownLatchTest {public static void main(String[] args) {testCase(); }public static void testCase() {CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 0; i < 6; i++) {new Thread(() ->{System.out.println(Thread.currentThread().getName()+"同學\t 離開了教室");countDownLatch.countDown();},String.valueOf(i)).start();}try {countDownLatch.await();System.out.println("班長最后走,并把教室燈關了,門鎖住====");} catch (InterruptedException e) {e.printStackTrace();}} }

    輸出如下:

    0同學 離開了教室 3同學 離開了教室 2同學 離開了教室 1同學 離開了教室 4同學 離開了教室 5同學 離開了教室 班長最后走,并把教室燈關了,門鎖住====

    CountDownLatch結合枚舉的使用

    現在模擬一個場景:秦始皇一統天下的過程。

  • 首先,春秋時代,一個有六個國家,所以你給的參數不能在6之外;
  • 六個國家先后被滅(此處忽略被滅順序);
  • 等六個國家全部滅了之后,秦始皇才一統天下,不能‘某一個國家被滅’出現在‘秦始皇已經一統天下’之后;
  • 上代碼:

    枚舉類:

    public enum CountryEnum {ONE(1,"齊"),TWO(2,"楚"),TRHEE(3,"燕"),FOUR(4,"趙"),FIVE(5,"魏"),SIX(6,"韓");private Integer code;private String country;//私有構造CountryEnum(Integer code, String country) {this.code = code;this.country = country;}//對外公開的方法,用于匹配public static String getCountryByCode(Integer code){CountryEnum[] countryValues = CountryEnum.values();for(CountryEnum country:countryValues){if(code == country.getCode()){return country.getCountry();}}return null;}getGetter/Setter方法略... }

    測試類:

    public class CountDownLatchTest {public static void main(String[] args) {actualUse();}public static void actualUse() {CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 1; i <= 6; i++) {new Thread(() ->{System.out.println(Thread.currentThread().getName()+"國\t 被滅了...");countDownLatch.countDown();},CountryEnum.getCountryByCode(i)).start();}try {countDownLatch.await();System.out.println("秦國一統天下======");} catch (InterruptedException e) {e.printStackTrace();}} }

    輸出如下:

    齊國 被滅了... 魏國 被滅了... 韓國 被滅了... 燕國 被滅了... 趙國 被滅了... 楚國 被滅了... 秦國一統天下======

    結合枚舉使用的好處:

    其實我們可以反過來推,如果這種情況不使用枚舉,他會怎么寫?

    肯定是在線程內部去判斷code值,類似這種:

    if(i == 1){System.out.println("齊國被滅了...") }else if(i == 1){System.out.println("楚國被滅了...") }else if(){ ... }

    這種問題在5個線程下還好,但如果50個呢,500個呢,這樣就很難維護。

    枚舉不僅可以簡化我們的代碼,降低維護成本,而且還可以限制傳參,比如,在當前代碼里,你傳一個7,他肯定是錯誤的。

    綜上所述,使用枚舉的好處:

  • 簡化代碼,降低后期維護成本;
  • 限制傳參類型,防止部分惡意傳參。
  • CountDownLatch原理

    由于這里本人對AQS的原理暫時還不清楚,所以這里不做詳細展示,

    后期待詳細了解之后,再來做詳細補充…

    本文的原理這塊都是總結性的東西,如想詳細研究,你可以通過其他資料查閱。

    CountDownLatch是通過維護一個計數器,然后通過AQS去實現阻塞與喚醒機制。

    當我們調用CountDownLatch countDownLatch = new CountDownLatch(N)的時候,他會將這個N傳遞給AQS隊列的state,而這個state的值代表CountDownLatch所剩余的計數次數。

    public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);//創建同步隊列,并設置初始計數器值(Sync為AQs的實現)}

    調用await()的時候,會創建一個節點,加入到AQS阻塞隊列,并同時把當前線程掛起。

    public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}

    調用countDownLatch.down()的時候,會對計數器進行減一操作,一旦為零,則喚醒所有阻塞隊列里面的線程。

    ?

    CyclicBarrier

    ?

    什么是CyclicBarrier呢?

    CyclicBarrierN個線程相互等待,任何一個線程在未完成之前,所有的線程都必須等待。

    還是老樣子,舉個通俗易懂的例子把。

    模擬場景:待收集完七顆龍珠,即可召喚神龍,缺任何一顆,都不行。

    常用方法

    //構造方法1 public CyclicBarrier(int parties) {this(parties, null); }//構造方法2 public CyclicBarrier(int parties, Runnable barrierAction) {if (parties <= 0) throw new IllegalArgumentException();this.parties = parties;this.count = parties;this.barrierCommand = barrierAction; } //await方法 public int await() throws InterruptedException, BrokenBarrierException {try {// 不超時等待return dowait(false, 0L);} catch (TimeoutException toe) {throw new Error(toe); // cannot happen} }

    ?

    代碼證明

    public class CyclicbarrierTest {public static final Integer CYCLICBARRIER_SIZE = 7;public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(CYCLICBARRIER_SIZE,()->{System.err.println("七顆龍珠已集齊,出來吧,神龍!");});for (int i = 1; i <= 7; i++) {new Thread(() ->{try {System.out.println("第"+Thread.currentThread().getName()+"顆龍珠已集齊!");cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}},String.valueOf(i)).start();}} }

    輸出結果:

    1顆龍珠已集齊! 第5顆龍珠已集齊! 第4顆龍珠已集齊! 第7顆龍珠已集齊! 第2顆龍珠已集齊! 第3顆龍珠已集齊! 第6顆龍珠已集齊! 七顆龍珠已集齊,出來吧,神龍!

    ?

    CyclicBarrier原理

    本文的原理這塊都是總結性的東西,如想詳細研究,你可以通過其他資料查閱。

    基于ReentrantLock和Condition來實現的。

    Semaphore

    ?

    什么是semaphore呢?

    semaphroe:信號量,一種共享鎖,指的是控制某個資源被訪問的線程數

    模擬場景:

  • 停車場共有3個車位,在同一時間內,有且僅能停3輛車;
  • 超出3輛車的部分則阻塞,即等待;
  • 當3個車位中有車駛出時,下面等待的車 則進來使用。
  • ?

    常用方法

    acquire() / acquire(int permits) 獲取1/多個憑證,如果憑證數量不夠,等待其他線程釋放(在未獲取到憑證之前、或者被其他線程調用中斷之前,該線程一直處于阻塞狀態。) 注意:此處的permits代表一個線程要占用2個憑證,如果總憑證為10,permits=2,只能同時5個線程占用。 ? acquireUninterruptibly() 獲取一個憑證,在獲取到憑證之前線程一直處于阻塞狀態(忽略中斷)。tryAcquire() / tryAcquire(int permits) 嘗試獲得憑證,返回獲取憑證成功或失敗,不阻塞線程。 ? tryAcquire(long timeout, TimeUnit unit) 嘗試獲得憑證,在超時時間內循環嘗試獲取,限時等待。 ? release() / release(int permits) 釋放1/多個憑證,喚醒1/多個獲取憑證不成功的阻塞線程。 ? hasQueuedThreads() 等待隊列里是否還存在等待線程。 ? getQueueLength() 獲取等待隊列里阻塞的線程數。 ? drainPermits() 清空憑證把可用憑證數置為0,返回清空憑證的數量。 ? availablePermits() 返回可用的憑證數量。

    代碼證明

    public class SemaphoreTest {public static final int SEMAPHORE_SIZE = 4;public static void main(String[] args) {Semaphore semaphore = new Semaphore(SEMAPHORE_SIZE);for (int i = 1; i <= 8; i++) {new Thread(() ->{try {System.out.println(Thread.currentThread().getName()+"車\t 來到了停車場 ");if(semaphore.availablePermits()==0){System.out.println("車位不足,請耐心等待");}semaphore.acquire();System.err.println(Thread.currentThread().getName()+"車\t搶到了車位");try{ TimeUnit.SECONDS.sleep(3);}catch(Exception e){e.getStackTrace();};System.out.println(Thread.currentThread().getName()+"車\t休息了3秒,開走了===");} catch (InterruptedException e) {e.printStackTrace();}finally{semaphore.release();}},String.valueOf(i)).start();}} }

    輸出結果:

    1車 來到了停車場 5車 來到了停車場 6車 來到了停車場 4車 來到了停車場 2車 來到了停車場 車位不足,請耐心等待 3車 來到了停車場 車位不足,請耐心等待 8車 來到了停車場 車位不足,請耐心等待 7車 來到了停車場 車位不足,請耐心等待 1車 搶到了車位 5車 搶到了車位 6車 搶到了車位 4車 搶到了車位 1車 休息了3秒,開走了=== 4車 休息了3秒,開走了=== 5車 休息了3秒,開走了=== 6車 休息了3秒,開走了=== 2車 搶到了車位 8車 搶到了車位 7車 搶到了車位 3車 搶到了車位 2車 休息了3秒,開走了=== 7車 休息了3秒,開走了=== 8車 休息了3秒,開走了=== 3車 休息了3秒,開走了===

    ?

    Semapore原理

    由于這里本人對AQS的原理暫時還不清楚,所以這里不做詳細展示,

    后期待詳細了解之后,再來做詳細補充…

    本文的原理這塊都是總結性的東西,如想詳細研究,你可以通過其他資料查閱。

    跟CountDownLatch一樣,也是維護了一個繼承了AQS的Sync同步器,對線程的控制均通過sync來實現。

    初始化時 Semaphore semaphore=new Semaphore(n)

    默認一個非公平鎖的同步阻塞隊列,他會將這個N傳遞給AQS隊列的state,代表我要申請憑證的數量。

    public Semaphore(int permits) {sync = new NonfairSync(permits);}

    acquire/realease則是線程的喚醒與阻塞機制…

    拓展

    ?

    CyclicBarrier與CountDownLatch的區別

  • 對于CountDownLatch來說,他的側重點在于一個線程在等待,而另外那N的線程在把**“某個事情”**做完之后可以繼續等待,可以終止。
    對于CyclicBarrier來說,他的側重點是在于N個線程,如果其中任何一個沒有完成,所有的線程都必須等待。
  • CountDownLatch的計數器只能使用一次,而CyclicBarrier的計數器可以使用reset()方法重置,可以使用多次,所以CyclicBarrier能夠處理更為復雜的場景;
  • 另:在semaphore與countDownLatch的比較中,前者可重復利用,就跟停車一樣,只要資源還在,就可以重復,但countDownLatch不可以,當count減完之后,不可再利用。

    ?

    總結

    其實本文只是一個簡單的使用,對于涉及到原理部分,由于暫時對AQS了解不深,不敢妄加評論。

    ?

    Renference

    關于java多線程淺析六: CyclicBarrier的原理分析和使用

    CountDownLatch的使用和原理解析

    Semaphore 使用及原理

    總結

    以上是生活随笔為你收集整理的Java并发辅助类的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。