主线程等待子线程执行完毕再执行的几种方式

简介: 主线程等待子线程执行完毕再执行的几种方式

一、CountDownLatch

使用CountDownLatch可以非常方便的实现线程等待,它是一种类似倒计时的功能,当计数为0时程序继续向下运行,初始化的时候需要指定计数数量,使用如下:

import java.time.LocalDateTime;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class CountDownLatchTest {
    public static void main(String[] args) throws Exception {
        int nums = 10;
        // 计数器
        CountDownLatch latch = new CountDownLatch(nums);
        
        for(int i = 0; i < nums; i++) {
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                  // 计数器 -1
                    latch.countDown();
                    System.out.println(Thread.currentThread().getName() + " run await : " + LocalDateTime.now());
                }
            }).start();
        }
        // 这里会阻塞程序执行,直到计数为0
        latch.await();
        System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
    }
}

二、ExecutorService

ExecutorService是通过线程池方式实现程序等待的,它的原理是进入方法时初始化一个线程池,添加任务执行,然后执行关闭线程池,线程池会在所有线程任务执行完成后进行关闭,通过判断线程池是否关闭来判断程序的执行。

import java.time.LocalDateTime;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class ExecutorServiceTest {
    public static void main(String[] args) throws Exception {
      // 初始化线程池
        ExecutorService service = Executors.newFixedThreadPool(5);
        for(int i = 0; i < 10; i ++) {
            service.execute(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        service.shutdown();
        // 通过循环判断线程池是否已经结束
//        while (!service.isTerminated()) {
//            TimeUnit.MILLISECONDS.sleep(5);
//        }
        // 通过阻塞等待线程池结束
        service.awaitTermination(20, TimeUnit.SECONDS);
        System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
    }

三、CompletableFuture

使用java8中的异步编程中的allOf()方法,可以等待指定的全部future执行完成后在继续执行主线程的方法:

import java.time.LocalDateTime;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
public class FutureTest {
    public static void main(String[] args) throws Exception {
        int nums = 10;
        CompletableFuture[] futures = new CompletableFuture[10];
        
        for(int i = 0; i < nums; i++) {
            futures[i] = CompletableFuture.runAsync(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        
        CompletableFuture<Void> future = CompletableFuture.allOf(futures);
        
        future.get();
        System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
    }
}

四、Thread的join()方法

join()方法会让主线程等待线程执行完成后再继续执行

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
public class JoinTest {
    public static void main(String[] args) {
        int nums = 10;
        Thread[] threads = new Thread[nums];
        for(int i = 0; i < nums; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                        System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            thread.start();
            threads[i] = thread;
        }
        Arrays.stream(threads).forEach(thread -> {
            try {
                thread.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
    }
}

五、CyclicBarrier

CyclicBarrier与CountDownLatch类似,但CyclicBarrier可重置。CyclicBarrier是所有线程执行完后一起等待,条件满足后所有线程再一起继续向下执行;而CountDownLatch是将计数减1,减数后程序可以继续执行,不用一起等待结束后再执行。

import java.time.LocalDateTime;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;
public class CyclicBarrierTest {
    public static void main(String[] args) {
        int nums = 10;
        CyclicBarrier cyclicBarrier = new CyclicBarrier(nums + 1);
        for(int i = 0; i < nums; i++) {
            new Thread(() -> {
                try {
                    TimeUnit.SECONDS.sleep(2);
                    System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
                    cyclicBarrier.await();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
        try {
            cyclicBarrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + " run end : " + LocalDateTime.now());
    }
}

CountDownLatch和CompletableFuture这两种方式比较简单,并且可以使用线程池来完成任务,线程重复使用减少系统开销。CyclicBarrier需要阻塞线程,ExecutorService每次都需要单独建立线程池执行任务,join()也是每个任务都需要单独建立线程执行,系统开销上会比较大。

相关文章
|
3天前
|
Java
java 主线程破获子线程异常
java 主线程破获子线程异常
42 0
|
6月前
|
C++ Windows
[√]window下子线程CCLOG导致主线程阻塞问题
[√]window下子线程CCLOG导致主线程阻塞问题
132 0
|
6月前
主线程等待子线程执行完毕再执行方法
主线程等待子线程执行完毕再执行方法
|
消息中间件 JavaScript 小程序
SpringBoot 使用线程池如何控制主线程和子线程的事务
SpringBoot 使用线程池如何控制主线程和子线程的事务
|
9月前
|
编译器
主线程退出对子线程影响
主线程退出对子线程影响
79 0
|
10月前
|
Python
python多线程----------主线程,子线程,任务讲解----拿下就是胜利
python多线程----------主线程,子线程,任务讲解----拿下就是胜利
100 0
|
11月前
|
Java
【java】主线程等待子线程执行结束后再执行,线程池
【java】主线程等待子线程执行结束后再执行,线程池
141 0
|
Java Android开发
【Android 异步操作】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 )
【Android 异步操作】Android 线程切换 ( 判定当前线程是否是主线程 | 子线程中执行主线程方法 | 主线程中执行子线程方法 )
447 0
使用ThreadPoolExecutor,当提交线程超过maximumPoolSize 会阻塞主线程吗?
使用ThreadPoolExecutor,当提交线程超过maximumPoolSize 会阻塞主线程吗?
135 0
使用ThreadPoolExecutor,当提交线程超过maximumPoolSize 会阻塞主线程吗?
|
Android开发
主线程中访问网络有限制?无法得到数据,必须另起线程
主线程中访问网络有限制?无法得到数据,必须另起线程
46 0