java.util.concurrent中的幾種同步工具類

  

  java.util.concurrent併發包中提供了一系列的的同步工具類,這些基礎類不論是否能在項目中使用到,瞭解一下使用方法和原理對java程序員來講都是有必要的。博主在看《java併發編程實戰》這本書中提到了其中幾個工具類,本文就對這些類進行簡單的描述。java

  CyclicBarrier(柵欄)

  4個朋友約好下班一塊兒玩吃雞,分別是M4,AWM,SKS,WIN94。這四個哥們下班時間不同,決定好一個時間一塊兒上號搞。程序員

 

  你們約好到家就開遊戲,必須珍惜生命,爭分奪秒玩遊戲。編程

  咱們用柵欄來模仿一下場景:  併發

 1 public class CyclicBarrierTest {
 2 
 3     private static CyclicBarrier barrier = new CyclicBarrier(4);
 4 
 5     private static class EatChickenPlayer extends Thread {
 6 
 7         private String name;
 8 
 9         //下班時間
10         private Long offWorkTime;
11 
12         public EatChickenPlayer(String name, Long time) {
13             this.name = name;
14             this.offWorkTime = time;
15         }
16 
17         @Override
18         public void run() {
19             Timer timer = new Timer();
20             timer.schedule(new TimerTask() {
21                 @Override
22                 public void run() {
23                     System.out.println(name + ":我上號了");
24                     try {
25                         barrier.await();
26                         System.out.println(name + ":開打開打");
27                     } catch (Exception e) {
28                         e.printStackTrace();
29                     }
30                 }
31             }, offWorkTime);
32         }
33     }
34 
35     public static void main(String[] args) {
36         EatChickenPlayer m416 = new EatChickenPlayer("m416", 3000L);
37         EatChickenPlayer AWM = new EatChickenPlayer("AWM", 6000L);
38         EatChickenPlayer SKS = new EatChickenPlayer("SKS", 9000L);
39         EatChickenPlayer win94 = new EatChickenPlayer("win94", 4000L);
40         m416.start();
41         AWM.start();
42         SKS.start();
43         win94.start();
44     }
45 }

  運行一下代碼,感覺一下,dom

  

  柵欄一般阻塞一系列線程,當全部須要的線程都達到柵欄位置,才能繼續執行。因此,柵欄能夠理解爲等待其餘的線程到達對應的位置,在一塊兒執行。ide

  在作一些併發測試的時候,有時候須要全部線程都執行到相應的位置,讓它們同時執行。並且柵欄是能夠重複利用的,當柵欄開放後,柵欄會進行重置,而後對後續的線程進行攔截。若是await調用超時,或者await的線程被中斷,柵欄就被認爲是打破了,全部await的線程都會停止而且拋出BrokenBarrierException。若是成功經過柵欄,那麼await將爲每一個線程返回一個惟一的到達索引號,咱們能夠經過索引來選舉一個領導線程。並在下一次循環中,經過領導線程執行一些特殊工做。工具

  CountDownLatch(閉鎖)

  好不容易,四我的都到到齊了,紛紛上號。要進行遊戲,必須等隊伍裏全部人都點擊準備完成,遊戲才能開始。這種時候能夠時候,咱們可使用閉鎖來實現這種業務場景。測試

 1 public class CountDownLatchTest {
 2 
 3     private static CountDownLatch countDownLatch = new CountDownLatch(4);
 4 
 5     public static class EatChicken extends Thread {
 6 
 7         private String name;
 8 
 9         private int time;
10 
11         public EatChicken(String name, int time) {
12             this.name = name;
13             this.time = time;
14         }
15 
16         @Override
17         public void run() {
18             System.out.println(name + "準備" + time + "秒");
19             try {
20                 SECONDS.sleep(time);
21             } catch (InterruptedException e) {
22                 e.printStackTrace();
23             }
24             System.out.println(name + "衣服換好了");
25             countDownLatch.countDown();
26         }
27     }
28     
29     public static void main(String[] args) throws InterruptedException {
30         System.out.println("-----等待全部人準備-----");
31         EatChicken m416 = new EatChicken("m416", 1);
32         EatChicken AWM = new EatChicken("AWM", 2);
33         EatChicken SKS = new EatChicken("SKS", 2);
34         EatChicken win94 = new EatChicken("win94", 3);
35         m416.start();
36         AWM.start();
37         SKS.start();
38         win94.start();
39 
40         countDownLatch.await();
41         System.out.println("-----全部人準備好了-----");
42         System.out.println("-----遊戲開始-----");
43     }

   運行程序:ui

  

  閉鎖初看跟柵欄很是類似,它們做爲工具類,都能做爲屏障,等待線程執行到必定的地方。閉鎖跟柵欄的區別,就是閉鎖是一次性的,當閉鎖徹底打開後就不能關閉了。柵欄打開以後,放行等待以後,柵欄就被重置,等待下一次開啓。閉鎖使用countDown()的時候,線程不會阻塞,繼續運行。柵欄沒有相似的countDown()方法,使用await()的時候,還須要等待的線程數-1,直到須要等待的線程數爲0的時候,柵欄打開。this

  Semaphore(信號量)

  有的時候,不只這四位老哥去玩,時不時的M24也要來一塊兒玩。這個時間,就只能你們一塊兒搶位置了。沒能擠進隊伍的老哥心裏百感交集,欲說還休。

  

  咱們能夠用信號量來模擬。

 1 public class SemaphoreTest {
 2 
 3     private static Semaphore semaphore = new Semaphore(4);
 4 
 5     private static class EatChickenPlayer extends Thread {
 6 
 7         private String name;
 8 
 9 
10         public EatChickenPlayer(String name) {
11             this.name = name;
12         }
13 
14         @Override
15         public void run() {
16             if (semaphore.tryAcquire()) {
17                 System.out.println(name + ":我進入遊戲啦");
18             } else {
19                 System.out.println(name + ":臥槽,隊伍滿了");
20             }
21         }
22     }
23 
24     public static void main(String[] args) throws InterruptedException {
25         EatChickenPlayer m416 = new EatChickenPlayer("m416");
26         EatChickenPlayer AWM = new EatChickenPlayer("AWM");
27         EatChickenPlayer SKS = new EatChickenPlayer("SKS");
28         EatChickenPlayer win94 = new EatChickenPlayer("win94");
29         EatChickenPlayer M24 = new EatChickenPlayer("M24");
30         m416.start();
31         AWM.start();
32         SKS.start();
33         win94.start();
34         M24.start();
35     }

 

  運行一下代碼:  

  

  信號量用來控制同時訪問某特定資源的操做數量。經過acquire()阻塞獲取資格。或者使用tryAcquire()方法獲取,及時返回結果,獲取權限成功則返回ture,獲取失敗返回false。當權限已經使用完畢後,調用release()或者release(int permits) 方法釋放權限。

  FutureTask

  遊戲終於開始了,落地的時候,發現有四個房子,四位胸懷吃雞的老哥分配好各走一個房子收集裏面的東西。收集出來以後一塊兒分東西。這裏把每一個老哥去房子裏舔裝備做爲一個Task。咱們用FutureTask來模擬一下這個場景:

  

  1 package com.chinaredstar.jc.lock;
  2 
  3 import java.util.ArrayList;
  4 import java.util.List;
  5 import java.util.Map;
  6 import java.util.Random;
  7 import java.util.concurrent.*;
  8 import java.util.stream.Collectors;
  9 
 10 public class FutureTaskTest {
 11 
 12     /**
 13      * 固定的4線程,線程池
 14      */
 15     private static ExecutorService threadPoolExecutor = Executors.newFixedThreadPool(4);
 16 
 17     static class Equipment {
 18         /**
 19          * 這裏簡單模擬一下,
 20          * 一、子彈 二、槍械 三、藥包
 21          */
 22         private Integer type;
 23 
 24         /**
 25          * 數量
 26          */
 27         private Integer num;
 28 
 29         public Equipment(Integer type, Integer num) {
 30             this.type = type;
 31             this.num = num;
 32         }
 33 
 34         public Integer getType() {
 35             return type;
 36         }
 37 
 38         public void setType(Integer type) {
 39             this.type = type;
 40         }
 41 
 42         public Integer getNum() {
 43             return num;
 44         }
 45 
 46         public void setNum(Integer num) {
 47             this.num = num;
 48         }
 49 
 50 
 51     }
 52 
 53     /**
 54      * 舔裝備人物類
 55      */
 56     static class CollectTask implements Callable {
 57         private String name;
 58 
 59         public CollectTask(String name) {
 60             this.name = name;
 61         }
 62 
 63         @Override
 64         public List<Equipment> call() throws Exception {
 65             return generatorEquipment(name);
 66         }
 67     }
 68 
 69     public static List<Equipment> generatorEquipment(String name) {
 70 
 71         List<Equipment> list = new ArrayList<>();
 72 
 73         Random r = new Random();
 74         //子彈
 75         Equipment bullet = new Equipment(1, r.nextInt(100));
 76         list.add(bullet);
 77         System.out.println(name + ":撿到子彈" + bullet.num + "發");
 78         //
 79         Equipment gun = new Equipment(2, r.nextInt(3));
 80         System.out.println(name + ":撿到槍" + gun.num + " 把");
 81         list.add(gun);
 82         //藥包
 83         Equipment bandage = new Equipment(3, r.nextInt(10));
 84         System.out.println(name + ":撿到繃帶" + bandage.num + " 個");
 85         list.add(bandage);
 86         return list;
 87     }
 88 
 89     public static void main(String[] args) throws ExecutionException, InterruptedException {
 90         //生成任務
 91         CollectTask AWM = new CollectTask("AWM");
 92         CollectTask m416 = new CollectTask("m416");
 93         CollectTask SKS = new CollectTask("SKS");
 94         CollectTask win94 = new CollectTask("win94");
 95         //生成任務交給線程池
 96         Future AWM_future = threadPoolExecutor.submit(AWM);
 97         Future m416_future = threadPoolExecutor.submit(m416);
 98         Future SKS_future = threadPoolExecutor.submit(SKS);
 99         Future win94_future = threadPoolExecutor.submit(win94);
100 
101         //結果放在一塊兒
102         List<Future> futureList = new ArrayList<>();
103         futureList.add(AWM_future);
104         futureList.add(m416_future);
105         futureList.add(SKS_future);
106         futureList.add(win94_future);
107 
108         //結果統一在一塊兒
109         List<Equipment> taskResultList = new ArrayList<>();
110         for (Future<List<Equipment>> future : futureList) {
111             taskResultList.addAll(future.get());
112         }
113 
114         //打印出來看看都有些啥
115         Map<Integer, Integer> tolal = taskResultList.stream().collect(Collectors.groupingBy(
116                 Equipment::getType, Collectors.summingInt(Equipment::getNum)));
117         tolal.entrySet();
118         for (Map.Entry<Integer, Integer> entry : tolal.entrySet()) {
119             if (entry.getKey() == 1) {
120                 System.out.println("總共有子彈:" + entry.getValue());
121             }
122             if (entry.getKey() == 2) {
123                 System.out.println("總共有槍:" + entry.getValue());
124             }
125             if (entry.getKey() == 3) {
126                 System.out.println("總共有藥包:" + entry.getValue());
127             }
128         }
129 
130     }
131 
132 }

  運行後的代碼結果: 

  

相關文章
相關標籤/搜索