上次简单了解了多线程中锁的类型,今天要简单了解下多线程并发控制的一些工具类了。

1. 概念说明:

CountDownLatch:相当于一个待执行线程计数器,当计数减为零时表示所有待执行线程都已执行完毕,等待被计数线程结果的(即使用await()的)线程可以继续执行(await()后面的代码)了。

CyclicBarrier:可循环屏障,相当于一个准备执行线程计数器,当计数减为零时表示所有待执行线程(这些线程需要同时开始工作)都已做好准备工作,可以开始工作了。

Semaphore:允许并发线程数,semaphore.acquire()和semaphore.release()方法总是成对出现,而这对方法之间的代码最多允许多少个线程访问是由Semaphore semaphore=new Semaphore(n)的n决定的。

2. 构造实际场景,使用示例

设想一个赛跑场景,一共有10个运动员参赛,所有运动员必须同时起跑(CyclicBarrier控制),而计分结束必须在所有运动员都跑完全程之后(CountDownLatch控制),但是学校操场只有4条跑道(Semaphore),那么就要让运动员分三批进行比赛,每次最多4人入场:

 import com.alibaba.fastjson.JSONObject;
 import org.apache.commons.lang3.concurrent.BasicThreadFactory;
 import org.junit.Test;

 import java.util.concurrent.*;

 public class MultiThreadsTest {
     private volatile ConcurrentHashMap<String, String> map = new ConcurrentHashMap<>();
     // 声明一个线程池,用于线程管理
     ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 4,
             20, TimeUnit.MICROSECONDS, new LinkedBlockingQueue<>(),
             new BasicThreadFactory.Builder().namingPattern("Runner-%d").build(),
             new ThreadPoolExecutor.CallerRunsPolicy());

     @Test
     public void testMultiControl() {
         // 跑道数
         int runways = 4;
         // 参赛总人数
         int runners = 10;
         // 参赛人数多于跑道数,需分批比赛
         int batch = 1;
         // 当运动员总数多于跑道数,最多runways人入场
         while (runners >= runways) {
             runningGame(batch++, runways);
             runners = runners - runways;
         }
         // 最后一批,还剩几个人,就几个人入场
         runningGame(batch, runners);
         System.out.println("\n所有选手都已跑完,现在宣布比赛结果:\n" + JSONObject.toJSONString(map));
     }

     private void runningGame(int batch, int runners) {
         System.out.println("第" + batch + "批,共" + runners + "人,运动员正在进场...");
         // 每批允许runways个运动员入场
         Semaphore semaphore = new Semaphore(runners);
         // 每批runways个运动员必须都准备好,同时起跑
         CyclicBarrier barrier = new CyclicBarrier(runners);
         // 每批runways个运动员都跑完全程,裁判这一批计分结束
         CountDownLatch latch = new CountDownLatch(runners);
         for (int i = runners; i > 0; i--) {
             // 每个运动员都是一个独立奔跑的线程
             executor.execute(() -> {
                 try {
                     // 每个运动员进场都会占用一个跑道
                     semaphore.acquire();
                     // 每个运动员就位都会通知barrier,等所有跑道上的运动员都就位,大家就一起跑!
                     barrier.await();
                     running(Thread.currentThread().getName());
                     // 每个运动员跑完都要让出自己的跑道(为下一批运动员让路)
                     semaphore.release();
                 } catch (InterruptedException e) {
                     e.printStackTrace();
                 } catch (BrokenBarrierException e) {
                     e.printStackTrace();
                 } finally {
                     // 每个运动员跑完都要通知裁判为自己计分
                     latch.countDown();
                 }
             });
         }
         try {
             // 跑道上所有运动员都跑完,则裁判计分结束
             latch.await();
             System.out.println("第" + batch + "批,共" + runners + "人,计分结束");
         } catch (InterruptedException e) {
             System.out.println(e.getStackTrace());
         }
     }

     private void running(String name) {
         long start = System.currentTimeMillis();
         System.out.println(name + " running started ...");
         System.out.println(name + ": I'm running.\taudience: Yes, I see.\t" + name + ":I'm done running.\taudience: Well done!");
         try {
             // 跑太快,歇会儿
             TimeUnit.MILLISECONDS.sleep(2);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         System.out.println(name + " running completed");
         // 裁判将所有运动员的分数都记录在map中
         map.put(name, (System.currentTimeMillis() - start) + "ms");
     }
 }

运行结果

3. 代码运行结果分析:

乍看上面代码和运行结果好像都没什么问题,每批允许4人进场,总是同时开始跑,也都等到每批选手都跑完后该批计分结束,countDownLatch和CyclicBarrier确实都起到了各自的作用,但是Semaphore呢?好像也确实是限制了只允许4个线程同时访问这段代码,然而在你一次只有4个选手的情况下,它当然是最多4个线程访问了,其实Semaphore的工作已经由方法入参给限定了啊,Semaphore的作用何在呢?那么假如学校原本有4个跑道,但是比赛开始之后却发现有一个入场通道坏了,变成了每次只能有3个人入场,但是场内的裁判并不知道,还在等待着按每4人一批进行比赛,那将会是什么结果呢?

我们修改代码看下:其他部分不变,只把允许入场的运动员数减1,然后再运行Test发现程序一直处于“第一批运动员进场”过程中,说明Semaphore这个变量已经起到了它的作用,就是每次只允许3个运动员进场,而且放入3个运动员后就会进行等待,等待这3个运动员比赛结束再放下一批进场,而场内的裁判并不知道可入场人数已经变化,还在等待着第4名选手入场才吹响开跑号声,因此入场管理员Semaphore和起跑裁判官CyclicBarrier就会陷入无法终止的相互等待,程序一直无法打破这种等待循环,因而进入停滞状态,这就是一种简单的死锁场景了。

4. 总结

这里只是简单说明这三个工具有用法,场景并不十分合理,其实这三者通常独立使用,一般不会碰到三者同时上场的机会。

Java多线程-CountDownLatch、CyclicBarrier、Semaphore的更多相关文章

  1. Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo

    Java并发编程工具类 CountDownLatch CyclicBarrier Semaphore使用Demo CountDownLatch countDownLatch这个类使一个线程等待其他线程 ...

  2. 并发包下常见的同步工具类详解(CountDownLatch,CyclicBarrier,Semaphore)

    目录 1. 前言 2. 闭锁CountDownLatch 2.1 CountDownLatch功能简介 2.2 使用CountDownLatch 2.3 CountDownLatch原理浅析 3.循环 ...

  3. CountDownLatch/CyclicBarrier/Semaphore 使用过吗?

    CountDownLatch/CyclicBarrier/Semaphore 使用过吗?下面详细介绍用法: 一,(等待多线程完成的)CountDownLatch  背景; countDownLatch ...

  4. 并发包下常见的同步工具类(CountDownLatch,CyclicBarrier,Semaphore)

    在实际开发中,碰上CPU密集且执行时间非常耗时的任务,通常我们会选择将该任务进行分割,以多线程方式同时执行若干个子任务,等这些子任务都执行完后再将所得的结果进行合并.这正是著名的map-reduce思 ...

  5. 转:java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例

    java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例 1.CountDownLatch:一个同步工具类,它允许一个或多个线程一 ...

  6. Java中的4个并发工具类 CountDownLatch CyclicBarrier Semaphore Exchanger

    在 java.util.concurrent 包中提供了 4 个有用的并发工具类 CountDownLatch 允许一个或多个线程等待其他线程完成操作,课题点 Thread 类的 join() 方法 ...

  7. 多线程中 CountDownLatch CyclicBarrier Semaphore的使用

    CountDownLatch 调用await()方法的线程会被挂起,它会等待直到count值为0才继续执行.也可以传入时间,表示时间到之后,count还没有为0的时候,就会继续执行. package ...

  8. 等待某(N)个线程执行完再执行某个线程的几种方法(Thread.join(),CountDownLatch,CyclicBarrier,Semaphore)

    1.main线程中先调用threadA.join() ,再调用threadB.join()实现A->B->main线程的执行顺序 调用threadA.join()时,main线程会挂起,等 ...

  9. CountDownLatch/CyclicBarrier/Semaphore 使用过吗

    CountDownLatch 让一些线程堵塞直到另一个线程完成一系列操作后才被唤醒.CountDownLatch 主要有两个方法,当一个或多个线程调用 await 方法时,调用线程会被堵塞,其他线程调 ...

  10. java 多线程 day15 CyclicBarrier 路障

    import java.util.concurrent.CyclicBarrier;import java.util.concurrent.ExecutorService;import java.ut ...

随机推荐

  1. javascript进阶-《原型对象和原型链》

    原创发布 by @一像素 2015.12 在Javascript中,万物皆对象,但对象也有区别,大致可以分为两类,即:普通对象Object 和 函数对象Function. 一般而言,通过new Fun ...

  2. 阿里云 RDS 数据库又发 CPU 近 100% 的“芯脏病”

    最近云界发生了2件事,一件是大事,一件是小事,大事是阿里云与微软合作推出了开放应用模型 Open Application Model(OAM),小事是由于微软 SQL Server 在阿里云上水土不服 ...

  3. Microsoft Word 2019 mac破解版下载

    Microsoft Word 2019 Mac版是大名鼎鼎的Office办公软件组件之一,能帮助你进行文字排版,可方便的进行创作项目.作业.信件.博客.剧本.笔记.评论文章或简历. Microsoft ...

  4. UVA1420 Priest John's Busiest Day【贪心】

    题意简介 有一个司仪,要主持n场婚礼,给出婚礼的起始时间和终止时间,每个婚礼需要超过一半的时间做为仪式,并且仪式不能终止.问说司仪能否主持n场婚礼. 输入格式 多组数据,每组数据输入一个\(N\)(\ ...

  5. phpstorm中xdebug配置和断点调试

    今天赶紧把这个搞了  运行环境: phpStorm 2018 PHP 5.45 nts VC9 Xdebug 2.4.1 0x01 PHP安装xdebug扩展 自己之前装一直没装起 原来是版本问题 下 ...

  6. 使用Prometheus监控SpringBoot应用

    通过之前的文章我们使用Prometheus监控了应用服务器node_exporter,数据库mysqld_exporter,今天我们来监控一下你的应用.(本文以SpringBoot 2.1.9.REL ...

  7. gitlab 提交

    gitlab 提交 Git global setup git config --global user.name "lial" git config --global user.e ...

  8. Javascript字符串常用方法详解

    字符串 字符串就是一个或多个排列在一起的字符,放在单引号或双引号之中. 'abc'"abc" length属性 js里的字符串类似于数组,都是一个一个字符拼凑在一起组成的,因此可以 ...

  9. Yii2.0教程应用结构篇 —— 入口脚本

    入口脚本是应用启动流程中的第一环,一个应用(不管是网页应用还是控制台应用)只有一个入口脚本.终端用户的请求通过入口脚本实例化应用并将将请求转发到应用. Web 应用的入口脚本必须放在终端用户能够访问的 ...

  10. collectionView reloadData走了不执行cellForItemAtIndexPath

    有可能是sizeForItemAtIndexPath方法中的前几个cell没有设置大小, 这里必须设置, 哪怕是设置一个很小的值