CountDownLatch的骚操作

CountDownLatch简介
以田径运动跑步为例,起跑前所有运动员等待裁判发枪声为准开始比赛、终点处计时裁判则需要等待所有运动员到达终点中宣布本轮次比赛结束,典型的多个线程等待一个线程、一个线程等待多个线程的场景,java.util.concurent包下的CountDownLatch就非常适合实现这种场景
CountDownLatch主要通过new CountdownLatch(n)的方式构建,调用await()方法的线程会进行挂起阻塞,通过countDown()方法进行计数器减一,直到n减为零为止,之前等待的线程就会恢复运行
CountDownLatch使用demo
以"起跑前所有运动员等待裁判发枪声准备起跑"的场景为例,模拟"多个线程等待一个线程":

/**
 * @desc:模拟多个线程等待单个线程:所有运动员等待裁判发出枪声
 **/
public class CountDownLatchTest1 {
 
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(7);
        CountDownLatch latch = new CountDownLatch(1);
        for (int i = 1; i < 8; i++) {
            int j = i;
            executorService.submit(() -> {
                try {
                    //所有运动员都得等待裁判发出枪声才能开始,所以先"挂起"
                    latch.await();
                    Thread.sleep((long) new Random().nextInt(1) + 1);
                    System.out.println("运动员" + j + "起跑");
                } catch (InterruptedException e) {
                }
            });
        }
        System.out.println("裁判发出枪响,比赛开始……");
        latch.countDown();
        executorService.shutdown();
    }
}
 
//输出结果:
裁判发出枪响,比赛开始……
运动员1起跑
运动员3起跑
运动员4起跑
运动员5起跑
运动员2起跑
运动员6起跑
运动员7起跑


以"终点处计时裁判则需要等待所有运动员到达终点中宣布本轮次比赛结束"的场景为例,模拟"单个线程等待多个线程":

/**
 * @desc:模拟单个线程等待多个线程:终点的裁判等待所有运动员都达到终点后宣布本批次结束
 **/
public class CountDownLatchTest2 {
 
    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        CountDownLatch latch = new CountDownLatch(7);
        for (int i = 1; i < 8; i++) {
            int j = i;
            executorService.submit(() -> {
                try {
                    Thread.sleep((long) new Random().nextInt(1) + 1);
                    System.out.println("运动员" + j + "到达");
                } catch (InterruptedException e) {
                } finally {
                    latch.countDown();
                }
            });
        }
        latch.await();
        System.out.println("所有运动员到达,裁判宣布本轮比赛结束!");
        executorService.shutdown();
    }
}
 
//输出结果:
运动员2到达
运动员1到达
运动员3到达
运动员4到达
运动员7到达
运动员5到达
运动员6到达

所有运动员到达,裁判宣布本轮比赛结束!

项目真实实例:

private List<UrlDto> getZipFileSystemUrls(List<FileDto> allFileDto) throws Exception {
        List<UrlDto> allUrlDtos = Lists.newArrayList();
        int threadCount = (int) allFileDto.stream().flatMap(k -> k.getFileList().stream()).filter(Objects::nonNull).count();
        CountDownLatch latch = new CountDownLatch(threadCount);
        Map<String, List<List<File>>> fileMap = allFileDto.stream().collect(Collectors.groupingBy(k -> k.getDataType(), Collectors.mapping(m -> m.getFileList(), Collectors.toList())));
        for (Map.Entry<String, List<List<File>>> entry : fileMap.entrySet()) {
            String dataType = entry.getKey();
            List<File> zipfiles = entry.getValue().stream().flatMap(k -> k.stream()).filter(Objects::nonNull).collect(Collectors.toList());
            for (File zipFile : zipfiles) {
                threadPoolTaskExecutor.execute(() -> {
                    BufferedInputStream bis = null;
                    try {
                        bis = new BufferedInputStream(new FileInputStream(zipFile));
                        byte[] fileBytes = new byte[bis.available()];
                        bis.read(fileBytes);
                        //上传到文件系统-->url
                        String fileUrl = com.smy.fsp.client.FileUtil.uploadBuilder().fileName(zipFile.getName()).fileByte(fileBytes).sceneType("hiveZip").upload();
                        UrlDto urlDto = UrlDto.builder().dataType(dataType).urlList(Lists.newArrayList(fileUrl)).build();
                        allUrlDtos.add(urlDto);
                    } catch (Exception e) {
                        log.error("压缩文件上传文件系统出现异常e:{}", e);
                    } finally {
                        latch.countDown();
                        if (bis != null) {
                            try {
                                bis.close();
                            } catch (IOException e) {
                                log.error("BufferedInputStream关闭流出现异常e:{}", e);
                            }
                        }
                    }
                });
            }
        }
        latch.await();
        Map<String, List<UrlDto>> groupUrlDto = allUrlDtos.stream().collect(Collectors.groupingBy(k -> k.getDataType()));
        List<UrlDto> urlDtos = groupUrlDto.keySet().stream().map(k -> {
            List<String> urls = Optional.ofNullable(groupUrlDto.get(k)).orElse(Lists.newArrayList()).stream().flatMap(m -> m.getUrlList().stream()).collect(Collectors.toList());
            return UrlDto.builder().urlList(urls).dataType(k).build();
        }).collect(Collectors.toList());
        return urlDtos;
    }


CyclicBarrer和CountDownLatc区别
1、CyclicBarrer的某个线程运行到屏障点后,线程立即停止运行,进入等待状态,直到所有的线程都达到了屏障点处,所有线程才继续运行;CountDownLatch则是当线程线程运行到某个点后,进行计数减1,该线程会继续运行;

2、CyclicBarrer可重复用(重置),CountDownLatch则不可以;

3、CyclicBarrer通过减计数方式,计数到达指定值时释放所有等待的线程,CountDownLatch通过加计数方式,计数为0时释放所有等待的线程;

4、CountDownLatch调用countDown()方法计数减一,调用await()方法只进行阻塞,对计数没任何影响,CyclicBarrer调用await()方法计数加1,若加1后的值不等于构造方法的值,则线程阻塞。
 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值