Java 异步编程——多线程都执行完再继续执行主流程(使用 CountDownLatch 类或调用 ExecutorService 的 invokeAll() 方法)


在使用 ThreadPoolExecutor 执行多个任务时,有时需要等待所有任务完成后再执行下一步操作。可以使用 ExecutorService 的 invokeAll() 方法或者结合 CountDownLatch 来实现这一功能。

  1. invokeAll() 方法:
    • 适用于 Callable 任务,简单明了地等待所有任务完成,并收集结果。
  2. CountDownLatch:
    • 适用于 Runnable 任务,提供了更灵活的控制,可以在任务执行过程中进行其他操作。

两种方式都能有效地保证在所有线程完成任务后再执行后续操作,选择哪种方式取决于具体的使用场景和任务类型。

CountDownLatch

CountDownLatch 是 Java 并发包中的一个同步辅助工具类,允许一个或多个线程等待直到一组操作完成。它可以用于实现任务的协调,确保某些线程在继续执行之前等待其他线程完成任务。

CountDownLatch 是一个计数的闭锁,作用与 CyclicBarrier 有点儿相似。CountDownLatch 是通过一个计数器来实现的,计数器的初始值为任务的数量。每当一个线程完成了一个任务后,计数器的值就会减1。当计数器值到达0时,它表示多线程已经完成了所有任务,然后就可以恢复等待的线程继续执行了。

工作原理:

  • 初始化时设置计数器的值为需要等待的线程任务数量。
  • 每当一个任务完成任务时,调用 countDown() 方法将计数器减 1。
  • 当计数器的值达到 0 时,所有等待的线程将被唤醒,继续执行。

使用场景

CountDownLatch 常用于以下场景:

  • 在所有线程完成某些子任务之前,不让主线程继续执行。
  • 等待多个线程执行完毕后再执行下一步操作。

示例:多个线程完成任务后主线程继续执行

import java.util.concurrent.CountDownLatch;

class MyRunnable implements Runnable {
    private final CountDownLatch latch;
    private final int taskId;

    public MyRunnable(CountDownLatch latch, int taskId) {
        this.latch = latch;
        this.taskId = taskId;
    }

    @Override
    public void run() {
        try {
            // 模拟任务执行
            System.out.println("Task " + taskId + " is running in thread: " + Thread.currentThread().getName());
            Thread.sleep((long) (Math.random() * 1000));
            System.out.println("Task " + taskId + " completed.");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            // 每执行完一个任务后,CountDownLatch 计数减一
            latch.countDown(); 
        }
    }
}

public class ThreadPoolExecutorCountDownLatchExample {
    public static void main(String[] args) {
        // 需要执行的任务数量
        int numberOfTasks = 10;
        // 初始化 CountDownLatch
        CountDownLatch latch = new CountDownLatch(numberOfTasks);
        ExecutorService executor = Executors.newFixedThreadPool(4);

        // 创建并提交任务
        for (int i = 1; i <= numberOfTasks; i++) {
            executor.submit(new MyRunnable(latch, i));
        }

        try {
            // 主线程等待所有任务完成
            latch.await(); 
            System.out.println("All tasks are completed. Main thread can continue...");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            executor.shutdown(); // Shutdown the executor
        }
    }
}

1、首先初始化 CountDownLatch 的计数器初始值为任务数量。

2、在任务 run 方法结束时调用 latch.countDown(),操作计数器减一。即每完成一个任务,CountDownLatch 的计数器减一。

3、主线程调用 latch.await(),等待计数器的值变为 0,在所有任务完成之前,主线程会被阻塞。一旦所有任务执行完,计数器减为 0,主线程将继续执行并打印消息。

使用 invokeAll() 方法

invokeAll 方法会阻塞当前线程,直到所有提交的任务完成,并返回一个包含每个任务结果的列表。此方法适用于 Callable 任务。

示例代码

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

class MyCallable implements Callable<Integer> {
    private final int taskId;

    public MyCallable(int taskId) {
        this.taskId = taskId;
    }

    @Override
    public Integer call() throws Exception {
        // 模拟任务执行
        System.out.println("Task " + taskId + " is running in thread: " + Thread.currentThread().getName());
        Thread.sleep((long) (Math.random() * 1000));
        System.out.println("Task " + taskId + " completed.");
        // 返回任务结果
        return taskId;
    }
}

public class ThreadPoolExecutorInvokeAllExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        List<Callable<Integer>> tasks = new ArrayList<>();

        // 创建并提交任务
        for (int i = 1; i <= 10; i++) {
            tasks.add(new MyCallable(i));
        }

        try {
            // 调用所有任务并等待其完成
            List<Future<Integer>> results = executor.invokeAll(tasks);
            for (Future<Integer> result : results) {
                // 获取任务执行结果
                System.out.println("Result: " + result.get()); 
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            executor.shutdown(); // Shutdown the executor
        }

        System.out.println("All tasks are completed. Main thread can continue...");
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值