主线程等待所有子线程结束的4种方法

这篇具有很好参考价值的文章主要介绍了主线程等待所有子线程结束的4种方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


主线程等待所有子线程结束的4种方法,包括使用 CountDownLatchCyclicBarrierFuture.get()Completable.allOf()

主线程不等待子线程全部结束

public class WaitThreadsDemo {
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 100L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            int index = i;
            executor.submit(() -> {
                System.out.println("子线程" + index);
            });
        }
        System.out.println("主线程-----");
    }
}

结果如下:

子线程0
子线程3
子线程5
主线程-----
子线程1
子线程7
子线程8
子线程9
子线程6
子线程4
子线程2

1、使用CountDownLatch

public class WaitThreadsDemo {
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 100L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();
        CountDownLatch count = new CountDownLatch(10);
        for (int i = 0; i < 10; i++) {
            int index = i;
            executor.submit(() -> {
                try {
                    Thread.sleep(200);
                    System.out.println("子线程" + index);
                    count.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        count.await();
        System.out.println("主线程-----");
        long end = System.currentTimeMillis();
        System.out.println("花费时间:" + (end - start));
    }
}

结果如下:

子线程4
子线程1
子线程0
子线程2
子线程3
子线程9
子线程5
子线程6
子线程7
子线程8
主线程-----
花费时间:493

2、同步屏障CyclicBarrier

2.1、CyclicBarrier使用

public class WaitThreadsDemo {
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 100L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        long start = System.currentTimeMillis();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(11);
        for (int i = 0; i < 10; i++) {
            int index = i;
            executor.submit(() -> {
                try {
                    Thread.sleep(200);
                    System.out.println("子线程running" + index);
                    cyclicBarrier.await();
                    System.out.println("子线程end***" + index);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
        System.out.println("主线程running@@@");
        cyclicBarrier.await();
        System.out.println("主线程end-----");
        long end = System.currentTimeMillis();
        System.out.println("花费时间:" + (end - start));
    }
}

结果如下:

主线程running@@@
子线程running5
子线程running7
子线程running6
子线程running8
子线程running9
子线程running1
子线程running0
子线程running2
子线程running3
子线程running4
子线程end***4
主线程end-----
花费时间:289
子线程end***6
子线程end***5
子线程end***7
子线程end***8
子线程end***0
子线程end***3
子线程end***2
子线程end***9
子线程end***1

由代码和执行结果可以看出:

  1. 屏障拦截数要算上主线程即:屏障拦截数量=子线程数量+主线程数量(11=10+1)
  2. 主线程也要调用await()
  3. 主线程会等待所有子线程到达屏障(调用await()
  4. 主线程无法控制子线程到达屏障(调用await())后的操作,所以子线程调用await()要放在最后
  5. 如果使用线程池,线程池核心线程数要大于等于屏障拦截数

2.2、CyclicBarrier复用

public class WaitThreadsDemo {
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 100L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws InterruptedException, BrokenBarrierException {
        long start = System.currentTimeMillis();
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
            System.out.println("主线程end----");
            long end = System.currentTimeMillis();
            System.out.println("花费时间:" + (end - start));
        });
        for (int i = 0; i < 10; i++) {
            int index = i;
            executor.submit(() -> {
                try {
                    Thread.sleep(200);
                    System.out.println("子线程running" + index);
                    cyclicBarrier.await();
                    System.out.println("子线程end***" + index);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        }
    }
}

结果如下:
java主线程等待子线程结束,Java多线程,java,jvm,面试

2.3、CountDownLatch和CyclicBarrier的区别

  • CountDownLatch表示所有子线程一个一个执行完毕,子线程执行完之前主线程阻塞。
  • CyclicBarrier表示所有子线程全部到达一个障碍屏障前,然后一起执行完毕,和主线程无关。
  • CountDownLatch基于AQS实现,CyclicBarrier基于ReenTrantLock + Condition实现
    就像田径比赛一样,CountDownLatch每次countDown()减一表示一个参赛者冲过终点线,全部通过,比赛结束。
    CyclicBarrier相当于在终点线前设置一个障碍,所有参赛者到达障碍前,然后撤掉障碍手拉手通过终点线。
    CyclicBarrier的复用就跟“车满发车”一个道理,乘客陆续上车,车满发车。

java主线程等待子线程结束,Java多线程,java,jvm,面试

3、使用Future.get()

public class WaitThreadsDemo {
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 100L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        long start = System.currentTimeMillis();
        List<Future> futureList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            int index = i;
            Future future = executor.submit(() -> {
                try {
                    Thread.sleep(200);
                    System.out.println("子线程running" + index);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
            futureList.add(future);
        }
        for (Future future : futureList) {
            //监听线程池子线程执行状态及执行结果。
            future.get();
        }
        System.out.println("主线程end-----");
        long end = System.currentTimeMillis();
        System.out.println("花费时间:" + (end - start));
    }
}

结果如下

子线程running7
子线程running4
子线程running6
子线程running5
子线程running1
子线程running3
子线程running2
子线程running8
子线程running9
子线程running0
主线程end-----
花费时间:303

4、使用Completable.allOf()

public class WaitThreadsDemo {
    private static ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 100L,
            TimeUnit.SECONDS, new LinkedBlockingQueue<>(), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws InterruptedException, ExecutionException {
        long start = System.currentTimeMillis();
        List<CompletableFuture> futureList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            int index = i;
            CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //模拟两次异常
                if (index % 7 ==0){
                    int tmp = index/0;
                }
                System.out.println("子线程running" + index);
                return "success";
            },executor).exceptionally(e-> e.getMessage());
            futureList.add(future);
        }
        //allOf()等待所有线程执行完毕
        CompletableFuture<Void> allFutures = CompletableFuture.allOf(futureList.toArray(new CompletableFuture[futureList.size()]));

        //但是allOf()拿不到执行结果,需要再次回调获取结果
        CompletableFuture<List<String>> finalFutures = allFutures.thenApply(v-> futureList.stream().map(CompletableFuture<String>::join).collect(Collectors.toList()));
        System.out.println(finalFutures.get());
        //也可以直接使用最初的futureList集合获取结果,如下两行:
        //List<String> resultList = futureList.stream().map(CompletableFuture<String>::join).collect(Collectors.toList());
        //System.out.println(resultList);
        
        System.out.println("主线程end-----");
        long end = System.currentTimeMillis();
        System.out.println("花费时间:" + (end - start));
    }
}

结果如下:

子线程running6
子线程running5
子线程running4
子线程running3
子线程running2
子线程running1
子线程running9
子线程running8
[java.lang.ArithmeticException: / by zero, success, success, success, success, success, success, java.lang.ArithmeticException: / by zero, success, success]
主线程end-----
花费时间:313

allOf()没有返回值,拿不到执行结果,需要再次使用回调函数获取最初futureList的执行结果。其实可以直接遍历futureList获取结果。
CompletableFuture的详细使用:CompletableFuture使用详解文章来源地址https://www.toymoban.com/news/detail-625229.html

到了这里,关于主线程等待所有子线程结束的4种方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java面试汇总——jvm篇

    目录 JVM的组成: 1、JVM 概述(⭐⭐⭐⭐) 1.1 JVM是什么? 1.2 JVM由哪些部分组成,运行流程是什么? 2、什么是程序计数器?(⭐⭐⭐⭐) 3、介绍一下Java的堆(⭐⭐⭐⭐) 4、虚拟机栈(⭐⭐⭐⭐) 4.1 什么是虚拟机栈? 4.2 垃圾回收是否涉及栈内存? 4.3 栈帧内存分配越大越好吗? 4.4

    2024年01月17日
    浏览(34)
  • java八股文面试[JVM]——JVM内存结构

    参考: JVM学习笔记(一)_卷心菜不卷Iris的博客-CSDN博客 JVM 是运行在操作系统之上的,它与硬件没有直接的交互 JVM内存结构:   方法区:存储已被虚拟机加载的类元数据信息(元空间) 堆:存放对象实例,几乎所有的对象实例都在这里分配内存 虚拟机栈:虚拟机栈描述的是

    2024年02月12日
    浏览(38)
  • JAVA 学习 面试(三)JVM篇

    JAVA虚拟机(JVM) JDK、JRE、JVM的关系 JDK JRE = Java虚拟机 + Java核心类库 JVM组成结构: (1)类加载器 (2)运行时数据区 (3)执行引擎 (4)本地库接口 类加载器 Java程序运行的时候,编译器将Java文件编译成平台无关的Java字节码文件(.class),接下来对应平台JVM对字节码文件进

    2024年01月22日
    浏览(41)
  • 3.Java面试题—JVM基础、内存管理、垃圾回收、JVM 调优

    一篇文章掌握整个JVM,JVM超详细解析!!! JVM (Java虚拟机) 是运行 Java 字节码 的 虚拟机 。 JVM 针对 不同系统 有 特定实现 ( Windows 、 Linux 等),目的是 同样的代码 在 不同平台 能运行出 相同的结果 。 Java 语言 要经过 编译 和 解释 两个步骤: 编译 :通过 编译器 将 代码 一

    2024年02月15日
    浏览(39)
  • JAVA工程师面试专题-JVM篇

    目录 一、运行时数据区 1、说一下JVM的主要组成部分及其作用? 2、说一下 JVM 运行时数据区 ? 3、说一下堆栈的区别 4、成员变量、局部变量、类变量分别存储在什么地方? 5、类常量池、运行时常量池、字符串常量池有什么区别? 6、JVM为什么使用元空间替换永久代 二、垃

    2024年02月21日
    浏览(34)
  • java八股文面试[JVM]——元空间

    JAVA8为什么要增加元空间 为什么要移除永久代?    知识来源: 【2023年面试】JVM8为什么要增加元空间_哔哩哔哩_bilibili

    2024年02月11日
    浏览(39)
  • JVM篇--Java内存区域高频面试题

    首先我们要知道java堆空间的产生过程: 即当通过java命令启动java进程的时候,就会为它分配内存,而分配内存的一部分就会用于创建堆空间,而当程序中创建对象的时候 就会从堆空间来分配内存,所以堆空间存放的主要是对象和数组; 而GC 其实说白了就是java虚拟机回收对象

    2024年02月01日
    浏览(41)
  • java八股文面试[JVM]——垃圾回收

    参考:JVM学习笔记(一)_卷心菜不卷Iris的博客-CSDN博客 GC垃圾回收 面试题: JVM内存模型 以及分区,需要详细到每个区放什么 堆里面的分区:Eden,survival from to,老年代,各自的特点。 GC的三种收集方法:标记清除、标记整理、复制算法的原理与特点,分别用在什么地方 针

    2024年02月11日
    浏览(35)
  • 在Java中如何确保在多线程情况下,所有线程都完成再继续执行任务?

    1.使用awaitTermination         awaitTermination是executorService自带的方法,用来确保所有的线程任务都执行完毕。 例如下使用线程池来执行100个不同的 SQL 语句,将查询结果存储在一个 List 集合中,具体实现如下: 定义一个 Runnable 接口的实现类,用来执行 SQL 查询任务,该类需要

    2024年02月11日
    浏览(26)
  • 大厂面试题一文讲通jvm,Java虚拟机高频面试题

    薪资范围:6-16K 一个类完整的生命周期,会经历五个阶段,分别为: 加载、连接、初始化、使用 、和 卸载 。其中的连接又分为 验证、准备 和 解析 三个步骤。如下图所示 加载(Loading) 简单一句话概括,类的加载阶段就是: 找到需要加载的类并把类的信息加载到jvm的方法

    2024年01月18日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包