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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

JUC强大的辅助工具类

發布時間:2023/12/18 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 JUC强大的辅助工具类 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

JUC中提供了一些輔助類,通過這些輔助類可以很好的解決線程數量過多時Lock鎖的頻繁操作。常用的三種輔助類有:

? CountDownLatch: 減少計數

? CyclicBarrier: 循環柵欄

? Semaphore: 信號燈

CountDownLatch

CountDownLatch是一個同步工具類,用來進行線程同步協作,等待所有線程完成倒計時。

CountDownLatch類可以設置一個計數器,然后通過countDown方法來進行減1的操作,使用await方法等待計數器不大于0,然后繼續執行await方法之后的語句。

? CountDownLatch主要有兩個方法,當一個或多個線程調用await方法時,這些線程會阻塞

? 其它線程調用countDown方法會將計數器減1(調用countDown方法的線程不會阻塞)

? 當計數器的值變為0時,因await方法阻塞的線程會被喚醒,繼續執行

實例一

場景: 6個同學陸續離開教室后值班同學才可以關門。

未使用CountDownLatch

package com.dongguo.juc;/*** @author Dongguo* @date 2021/9/4 0004-8:51* @description: */ public class Demo {public static void main(String[] args) {for (int i = 1; i <= 6; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "離開教室");}, "t" + i).start();}System.out.println("班長鎖門");} } 運行結果 班長鎖門 t1離開教室 t2離開教室 t3離開教室 t4離開教室 t5離開教室 t6離開教室

發現邏輯上出了問題 ,班長鎖門,之后的同學就無法離開教室,所以這個實現是失敗的。

使用CountDownLatch

package com.dongguo.juc;import java.util.concurrent.CountDownLatch;/*** @author Dongguo* @date 2021/9/4 0004-8:51* @description:*/ public class Demo {public static void main(String[] args) {CountDownLatch countDownLatch = new CountDownLatch(6);for (int i = 1; i <= 6; i++) {new Thread(()->{countDownLatch.countDown();System.out.println(Thread.currentThread().getName()+"離開教室");},"t"+i).start();}try {countDownLatch.await();System.out.println("班長鎖門");} catch (InterruptedException e) {e.printStackTrace();}} } 運行結果 t2離開教室 t3離開教室 t1離開教室 t4離開教室 t5離開教室 t6離開教室 班長鎖門

可以看到班長等到6位同學全部走光了才會鎖門。

我們還可以配合線程池使用,改進如下

package com.dongguo.juc;import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;/*** @author Dongguo* @date 2021/8/24 0024-16:03* @description: CountDownLatch*/ public class CountDownLatchDemo2 {/*** 6個同學陸續離開教室后值班同學才可以關門*/public static void main(String[] args) throws Exception {ExecutorService service = Executors.newFixedThreadPool(4);//定義一個數值為6的計數器CountDownLatch countDownLatch = new CountDownLatch(6);// 創建6個同學for (int i = 1; i <= 6; i++) {try {int count =i;service.submit(() -> {if (Thread.currentThread().getName().equals("同學6")) {try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() +"同學"+count+ "離開了"); //計數器減一,不會阻塞countDownLatch.countDown();});} catch (Exception e) {e.printStackTrace();}}//主線程await休息System.out.println("主線程睡覺");countDownLatch.await();//主線程被喚醒//全部離開后自動喚醒主線程System.out.println("全部離開了,現在的計數器為" + countDownLatch.getCount());service.shutdown();} } 運行結果 主線程睡覺 pool-1-thread-1同學1離開了 pool-1-thread-3同學3離開了 pool-1-thread-3同學5離開了 pool-1-thread-1同學6離開了 pool-1-thread-4同學4離開了 pool-1-thread-2同學2離開了 全部離開了,現在的計數器為0

實例二

模擬10個人匹配LOL進入加載游戲 ,等待10個人全部加載100%后游戲開始

package com.dongguo.juc;import java.util.Arrays; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors;/*** @author Dongguo* @date 2021/8/24 0024-16:03* @description: 模擬10個人匹配LOL進行加載游戲 ,等待10個人全部加載100%后游戲開始*/ public class CountDownLatchDemo3 {public static void main(String[] args) throws Exception {ExecutorService service = Executors.newFixedThreadPool(10);Random random = new Random();String[] all = new String[10];CountDownLatch countDownLatch = new CountDownLatch(10);for (int j = 0; j < 10; j++) {//10人int k = j;service.submit(()->{for (int i = 0; i <=100 ; i++) {try {//隨機睡眠100毫秒以內Thread.sleep(random.nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}all[k] = i+"%";System.out.print("\r"+ Arrays.toString(all));}countDownLatch.countDown();});}countDownLatch.await();System.out.println("\n游戲開始");service.shutdown();} } 運行結果 [100%, 100%, 100%, 100%, 100%, 100%, 100%, 100%, 100%, 100%] 游戲開始

實例三

在分布式系統中,經常會出現多次遠程調用不同的服務的場景,

比如查詢訂單,需要調用訂單信息,商品信息,物流信息等服務

package com.dongguo.juc;import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController;import java.util.HashMap; import java.util.Map;/*** @author Dongguo* @date 2021/9/13 0013-16:50* @description: 模擬三個服務請求*/ @RestController public class CountDownlatchController {/*** 訂單服務* @param id* @return*/@GetMapping("/order/{id}")public Map<String, Object> order(@PathVariable int id) {HashMap<String, Object> map = new HashMap<>();map.put("id", id);map.put("total", "2300.00");sleep(2000);return map;}/*** 商品服務* @param id* @return*/@GetMapping("/product/{id}")public Map<String, Object> product(@PathVariable int id) {HashMap<String, Object> map = new HashMap<>();if (id == 1) {map.put("name", "小愛音箱");map.put("price", 300);} else if (id == 2) {map.put("name", "小米手機");map.put("price", 2000);}map.put("id", id);sleep(1000);return map;}/*** 物流服務* @param id* @return*/@GetMapping("/logistics/{id}")public Map<String, Object> logistics(@PathVariable int id) {HashMap<String, Object> map = new HashMap<>();map.put("id", id);map.put("name", "中通快遞");sleep(2500);return map;}private void sleep(int millis) {try {Thread.sleep(millis);} catch (InterruptedException e) {e.printStackTrace();}} }

未使用CountDownLatch

package com.dongguo.juc;import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate;import java.util.Map;/*** @author Dongguo* @date 2021/9/13 0013-16:49* @description:*/ @Slf4j(topic = "d.CountDownLatchDemo4") public class CountDownLatchDemo4 {public static void main(String[] args) {RestTemplate restTemplate = new RestTemplate();log.debug("begin");Map<String,Object> response = restTemplate.getForObject("http://localhost:8080/order/{1}", Map.class, 1);log.debug("result order:{}",response);Map<String,Object> response2 = restTemplate.getForObject("http://localhost:8080/product/{1}", Map.class, 1);Map<String,Object> response3 = restTemplate.getForObject("http://localhost:8080/product/{2}", Map.class, 2);log.debug("result product:{}",response2,response3);Map<String,Object> response4 = restTemplate.getForObject("http://localhost:8080/logistics/{1}", Map.class, 1);log.debug("result logistics:{}",response4);} } 運行結果 17:28:40 [main] d.CountDownLatchDemo4 - begin 17:28:43 [main] d.CountDownLatchDemo4 - result order:{total=2300.00, id=1} 17:28:45 [main] d.CountDownLatchDemo4 - result product:{price=300, name=小愛音箱, id=1} 17:28:47 [main] d.CountDownLatchDemo4 - result logistics:{name=中通快遞, id=1}

這樣串行的請求,效率是非常的,一共花費7S

使用CountDownLatch+線程池

package com.dongguo.juc;import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate;import java.util.Map; import java.util.concurrent.*;/*** @author Dongguo* @date 2021/9/13 0013-16:49* @description:*/ @Slf4j(topic = "d.CountDownLatchDemo4") public class CountDownLatchDemo4 {public static void main(String[] args) {RestTemplate restTemplate = new RestTemplate();ExecutorService service = Executors.newCachedThreadPool();CountDownLatch countDownLatch = new CountDownLatch(4);log.debug("begin");service.submit(() -> {Map<String,Object> response =restTemplate.getForObject("http://localhost:8080/order/{1}", Map.class, 1);log.debug("result order:{}",response);countDownLatch.countDown();});service.submit(() -> {Map<String,Object> response2 =restTemplate.getForObject("http://localhost:8080/product/{1}", Map.class, 1);log.debug("result product:{}",response2);countDownLatch.countDown();});service.submit(() -> {Map<String,Object> response2 =restTemplate.getForObject("http://localhost:8080/product/{1}", Map.class, 2);log.debug("result product:{}",response2);countDownLatch.countDown();});service.submit(() -> {Map<String,Object> response4 =restTemplate.getForObject("http://localhost:8080/logistics/{1}", Map.class, 1);log.debug("result logistics:{}",response4);countDownLatch.countDown();});try {countDownLatch.await();log.debug("執行完畢");} catch (InterruptedException e) {e.printStackTrace();} catch (Exception e) {e.printStackTrace();}finally {service.shutdown();}} } 運行結果 17:43:28 [main] d.CountDownLatchDemo4 - begin 17:43:29 [pool-1-thread-2] d.CountDownLatchDemo4 - result product:{price=300, name=小愛音箱, id=1} 17:43:29 [pool-1-thread-3] d.CountDownLatchDemo4 - result product:{price=2000, name=小米手機, id=2} 17:43:30 [pool-1-thread-1] d.CountDownLatchDemo4 - result order:{total=2300.00, id=1} 17:43:31 [pool-1-thread-4] d.CountDownLatchDemo4 - result logistics:{name=中通快遞, id=1} 17:43:31 [main] d.CountDownLatchDemo4 - 執行完畢

一共花費3s

但是這些打印信息都是在線程池的工作線程中打印的并沒有將信息返回給main線程

使用Future 的get()獲得信息

package com.dongguo.juc;import lombok.extern.slf4j.Slf4j; import org.springframework.web.client.RestTemplate;import java.util.Map; import java.util.concurrent.*;/*** @author Dongguo* @date 2021/9/13 0013-16:49* @description:*/ @Slf4j(topic = "d.CountDownLatchDemo4") public class CountDownLatchDemo4 {public static void main(String[] args) {RestTemplate restTemplate = new RestTemplate();ExecutorService service = Executors.newCachedThreadPool();log.debug("begin");Future<Map<String,Object>> f1 = service.submit(() -> {Map<String, Object> r =restTemplate.getForObject("http://localhost:8080/order/{1}", Map.class, 1);return r;});Future<Map<String, Object>> f2 = service.submit(() -> {Map<String, Object> r =restTemplate.getForObject("http://localhost:8080/product/{1}", Map.class, 1);return r;});Future<Map<String, Object>> f3 = service.submit(() -> {Map<String, Object> r =restTemplate.getForObject("http://localhost:8080/product/{1}", Map.class, 2);return r;});Future<Map<String, Object>> f4 = service.submit(() -> {Map<String, Object> r =restTemplate.getForObject("http://localhost:8080/logistics/{1}", Map.class, 1);return r;});try {System.out.println(f1.get());System.out.println(f2.get());System.out.println(f3.get());System.out.println(f4.get());log.debug("執行完畢");} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}finally {service.shutdown();}} } 17:58:40 [main] d.CountDownLatchDemo4 - begin {total=2300.00, id=1} {price=300, name=小愛音箱, id=1} {price=2000, name=小米手機, id=2} {name=中通快遞, id=1} 17:58:43 [main] d.CountDownLatchDemo4 - 執行完畢

CountDownLatch是一次性的,初始化后,進行業務操作,計數器歸零,則這個實例使命完成,無法服務這個實例。如果想要循環使用那就可以選擇CyclicBarrier

CyclicBarrier

CyclicBarrier看英文單詞可以看出大概就是循環阻塞的意思,在使用中CyclicBarrier的構造方法第一個參數是目標障礙數,每次執行CyclicBarrier一次 障礙數會加一,如果達到了目標障礙數,才會執行cyclicBarrier.await()之后的語句。可以將CyclicBarrier 的await()理解為加1操作

場景: 集齊7顆龍珠就可以召喚神龍

package com.dongguo.juc;import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;/*** @author Dongguo* @date 2021/9/4 0004-8:51* @description:*/ public class Demo {//定義神龍召喚需要的龍珠總數private final static int NUMBER = 7;public static void main(String[] args) {CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER, () -> {System.out.println("集齊7顆龍珠召喚神龍");});for (int i = 1; i <= 7; i++) {int temp = i;new Thread(() -> {try {System.out.println(Thread.currentThread().getName() + "收集到第" + temp + "顆龍珠");cyclicBarrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}}, "t" + i).start();}} } 運行結果 t1收集到第1顆龍珠 t2收集到第2顆龍珠 t3收集到第3顆龍珠 t4收集到第4顆龍珠 t5收集到第5顆龍珠 t6收集到第6顆龍珠 t7收集到第7顆龍珠 集齊7顆龍珠就可以召喚神龍

Semaphore

Semaphore的構造方法中傳入的第一個參數是最大信號量(可以看成最大線程池),每個信號量初始化為一個最多只能分發一個許可證。使用acquire方法獲得許可證,release方法釋放許可

Semaphore信號量,用來限制能同時訪問共享資源的線程上限

在概念上,信號量維持一組許可證。如果有必要,每個acquire()都會阻塞,直到許可證可用,然后才能使用它。每個release()添加許可證,潛在地釋放阻塞獲取方。但是,沒有使用實際的許可證對象;Semaphore只保留可用數量的計數,并相應地執行。即一個Semaphore維護了一組permits【許可證】。每次調用acquire()方法都會阻塞,直到獲取到許可證。每次調用release()方法都會添加一個許可證,也就是釋放一個被阻塞的獲取者。但是實際上并不存在這個許可證,Semaphore僅僅是記錄可用資源的數量,并且做出對應的行為(有資源就獲取,沒有資源就阻塞)。

信號量通常用于限制線程數,而不是訪問某些(物理或邏輯)資源。

  • 線程池控制的是線程數量,而信號量控制的是并發數量,雖然說看起來一樣,但兩者還是有區別的。
  • 信號量類似于鎖機制,信號量的調用,當達到數量后,線程還是存在的,只是被掛起了而已。而線程池,同時執行的線程數量是固定的,超過了數量的只能等待。

在獲得項目之前,每個線程必須從信號量獲取許可證,以確保某個項目可用。當線程完成該項目后,它將返回到池中,并將許可證返回到信號量,允許另一個線程獲取該項目。請注意,**當調用acquire()時,不會保持同步鎖定,因為這將阻止某個項目返回到池中。**信號量封裝了限制對池的訪問所需的同步,與保持池本身一致性所需的任何同步分開。【即將限制對池的訪問和對池中數據的操作所需要的鎖分開】。

信號量被初始化為一個,并且被使用,使得它只有至多一個允許可用,可以用作互斥鎖。這通常被稱為二進制信號量,因為它只有兩個狀態:一個許可證可用,或零個許可證可用。當以這種方式使用時,二進制信號量具有屬性(與許多Lock實現不同),“鎖”可以由除所有者之外的線程釋放(因為信號量沒有所有權概念)。這在某些專門的上下文中是有用的,例如死鎖恢復。

Semaphore(int permits) 創建一個 Semaphore與給定數量的許可證和非公平公平設置。 Semaphore(int permits, boolean fair) 創建一個 Semaphore與給定數量的許可證和給定的公平設置。

此類的構造函數可選擇接受公平參數。當設置為false時,此類不會保證線程獲取許可的順序。特別是,闖入是允許的,也就是說,一個線程調用acquire()可以提前已經等待線程分配的許可證-在等待線程隊列的頭部邏輯新的線程將自己【新線程將自己放在等待線程隊列的最前面】。當公平設置為真時,信號量保證調用acquire方法的線程被選擇以按照它們調用這些方法的順序獲得許可(先進先出; FIFO)【FIFO的順序是指是依據到達方法內部的執行點的時間,并不是方法執行的時間。】。請注意,FIFO排序必須適用于這些方法中的特定內部執行點。因此,一個線程可以在另一個線程之前調用acquire,但是在另一個線程之后到達排序點,并且類似地從方法返回。另請注意,未定義的tryAcquire方法不符合公平性設置,但將采取任何可用的許可證。【不定時的tryAcquire()方法會任意選取可用的許可證。】【非公平鎖可以插隊獲取運行,公平鎖按照線程順序執行。

通常,用于控制資源訪問的信號量應該被公平地初始化,以確保線程沒有被訪問資源【確保沒有線程因為長時間獲取不到許可證而餓死】。當使用信號量進行其他類型的同步控制時,非正常排序的吞吐量優勢往往超過公平性。

場景: 搶車位, 10部汽車3個停車位

package com.dongguo.juc;import java.util.Timer; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit;/*** @author Dongguo* @date 2021/9/4 0004-8:51* @description:*/ public class Demo {private final static int NUMBER = 3;public static void main(String[] args) {//定義3個停車位Semaphore semaphore =new Semaphore(NUMBER);for (int i = 1; i <=10; i++) {new Thread(()->{try {TimeUnit.MILLISECONDS.sleep(100);//找車位System.out.println(Thread.currentThread().getName() + "找車位ing");semaphore.acquire();System.out.println(Thread.currentThread().getName() + "汽車停車成功!");TimeUnit.SECONDS.sleep(3);//停車時間} catch (InterruptedException e) {e.printStackTrace();} finally {System.out.println(Thread.currentThread().getName() + "離開停車位");semaphore.release();}},"t"+i).start();}} } 運行結果: t1找車位ing t1汽車停車成功! t2找車位ing t2汽車停車成功! t3找車位ing t3汽車停車成功! t4找車位ing t5找車位ing t6找車位ing t7找車位ing t9找車位ing t8找車位ing t10找車位ing t1離開停車位 t4汽車停車成功! t2離開停車位 t5汽車停車成功! t3離開停車位 t6汽車停車成功! t4離開停車位 t7汽車停車成功! t5離開停車位 t9汽車停車成功! t6離開停車位 t8汽車停車成功! t7離開停車位 t10汽車停車成功! t9離開停車位 t8離開停車位 t10離開停車位

總結

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

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