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

目录

    • 主线程不等待子线程全部结束
    • 1、使用CountDownLatch
    • 2、同步屏障CyclicBarrier
      • 2.1、CyclicBarrier使用
      • 2.2、CyclicBarrier复用
      • 2.3、CountDownLatch和CyclicBarrier的区别
    • 3、使用Future.get()
    • 4、使用Completable.allOf()

主线程等待所有子线程结束的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();
                }
            });
        }
    }
}

结果如下:
主线程等待所有子线程结束的4种方法_第1张图片

2.3、CountDownLatch和CyclicBarrier的区别

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

主线程等待所有子线程结束的4种方法_第2张图片

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 resultList = futureList.stream().map(CompletableFuture::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使用详解

你可能感兴趣的:(Java多线程,java,jvm,面试)