• java.util.concurrent中的几种同步工具类


      

      java.util.concurrent并发包中提供了一系列的的同步工具类,这些基础类不管是否能在项目中使用到,了解一下使用方法和原理对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 }

      运行一下代码,感受一下,

      

      栅栏通常阻塞一系列线程,当所有需要的线程都达到栅栏位置,才能继续执行。所以,栅栏可以理解为等待其他的线程到达对应的位置,在一起执行。

      在做一些并发测试的时候,有时候需要所有线程都执行到相应的位置,让它们同时执行。而且栅栏是可以重复利用的,当栅栏开放后,栅栏会进行重置,然后对后续的线程进行拦截。如果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     }

       运行程序:

      

      闭锁初看跟栅栏非常相似,它们作为工具类,都能作为屏障,等待线程执行到一定的地方。闭锁跟栅栏的区别,就是闭锁是一次性的,当闭锁完全打开后就不能关闭了。栅栏打开之后,放行等待之后,栅栏就被重置,等待下一次开启。闭锁使用countDown()的时候,线程不会阻塞,继续运行。栅栏没有类似的countDown()方法,使用await()的时候,还需要等待的线程数-1,直到需要等待的线程数为0的时候,栅栏打开。

      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          * 1、子弹 2、枪械 3、药包
     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 }

      运行后的代码结果: 

      

      

      

  • 相关阅读:
    layui使用iconfont
    MySQL&MyBatis 时间处理的配合
    Jira客户端
    同级div设置display:inline-block,父级div强制不换行
    Spring RestController 请求参数详解
    Mybatis映射文件
    Redis分布式锁
    Netty1
    Maven工程打成一个jar包
    Maven 工程读取resource下的文件
  • 原文地址:https://www.cnblogs.com/null-qige/p/9337656.html
Copyright © 2020-2023  润新知