当前位置: 首页 > article >正文

for循环内线程池并发执行任务,等到子线程全部处理完任务,主线程在执行java的实现方式

for循环内线程池并发执行任务,等到子线程全部处理完任务,主线程在执行

方式一 使用 CountDownLatch

在 Java 中,您可以使用 CountDownLatch 来实现主线程等待子线程执行完成的功能。CountDownLatch 是一个同步工具类,它允许一个或多个线程等待其他线程完成操作后再继续执行。

具体来说,您可以在每个子线程中创建一个 CountDownLatch 对象,并将计数器初始化为 1。当子线程处理完任务后,调用 CountDownLatch.countDown() 方法,将计数器减 1。在主线程中,调用 CountDownLatch.await() 方法阻塞等待所有子线程的任务执行完毕,直到计数器减为 0 才继续执行后续代码。

以下是一个简单的示例代码:

ExecutorService executor = Executors.newFixedThreadPool(5); // 创建拥有 5 个线程的线程池

CountDownLatch latch = new CountDownLatch(5); // 创建计数器,初始值为 5

for (int i = 0; i < 5; i++) {
    executor.submit(() -> {
        // 子线程处理任务
        doTask();

        latch.countDown(); // 计数器减 1
    });
}

try {
    latch.await(); // 等待所有子线程的任务执行完毕
} catch (InterruptedException e) {
    // 处理中断异常
}

// 所有子线程的任务都已经执行完毕,执行主线程的代码
doSomethingElse();

请注意,在实际应用中,您还需要考虑线程安全、并发访问等问题,并在代码中正确处理异常。例如,在子线程处理任务时,可能需要使用锁或其他同步机制来保护共享资源的访问;在主线程等待子线程执行完毕时,可能需要处理 InterruptedException 异常等异常情况。

方式二 使用 ExecutorService 的 invokeAll() 方法

如果您不想使用 CountDownLatch,还有其他方法可以实现主线程等待子线程任务执行完毕的功能。以下是一些常见的方式:

使用 ExecutorService 的 invokeAll() 方法
ExecutorService 中的 invokeAll() 方法可以提交多个 Callable 或 Runnable 任务,并阻塞等待所有任务完成。该方法返回一个 Future 对象列表,可以通过遍历列表获取各个子线程处理任务后的结果。

以下是一个使用 invokeAll() 方法的示例代码

ExecutorService executor = Executors.newFixedThreadPool(5); // 创建拥有 5 个线程的线程池

List<Callable<Void>> tasks = new ArrayList<>();
for (int i = 0; i < 5; i++) {
    tasks.add(() -> {
        // 子线程处理任务
        doTask();
        return null;
    });
}

executor.invokeAll(tasks); // 等待所有子线程的任务执行完毕

// 所有子线程的任务都已经执行完毕,执行主线程的代码
doSomethingElse();

方式三 使用 CompletableFuture 的 allOf() 方法

Java 8 中引入了 CompletableFuture 类,它提供了一系列基于回调函数的异步编程方法,可以方便地实现多线程任务的并发执行和结果组合。其中,allOf() 方法可以用于等待所有 CompletableFuture 对象的计算结果。

以下是一个使用 CompletableFuture 的 allOf() 方法的示例代码:

ExecutorService executor = Executors.newFixedThreadPool(5); // 创建拥有 5 个线程的线程池

List<CompletableFuture<Void>> futures = new ArrayList<>();
for (int i = 0; i < 5; i++) {
    CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
        // 子线程处理任务
        doTask();
    }, executor);
    futures.add(future);
}

CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join(); // 等待所有子线程的任务执行完毕

// 所有子线程的任务都已经执行完毕,执行主线程的代码
doSomethingElse();

请注意,在使用以上方法时,仍然需要考虑线程安全、并发访问等问题,并在代码中正确处理异常。

方式四

ThreadPoolExecutor executor = new ThreadPoolExecutor(
    5, // corePoolSize
    5, // maximumPoolSize
    0L, TimeUnit.MILLISECONDS, // keepAliveTime, unit
    new LinkedBlockingQueue<Runnable>()); // workQueue

AtomicInteger count = new AtomicInteger(5); // 计数器,初始值为 5

for (int i = 0; i < 5; i++) {
    executor.execute(() -> {
        // 子线程处理任务
        doTask();

        count.decrementAndGet(); // 计数器减 1
    });
}

while (count.get() > 0) {
    Thread.sleep(100); // 等待一段时间,避免空转浪费 CPU 资源
}

// 所有子线程的任务都已经执行完毕,执行主线程的代码
doSomethingElse();

在上述代码中,我们创建了一个 ThreadPoolExecutor 对象,然后通过 AtomicInteger 实现了一个计数器,初始值为 5。在每个子线程执行完任务后,计数器的值减 1。在主线程中使用 while 循环检查计数器是否为 0,如果未达到目标,则阻塞等待一段时间,避免空转浪费 CPU 资源。当计数器减为 0 时,所有子线程的任务都已经执行完毕,主线程可以执行后续代码。

请注意,在使用自定义线程池时,需要考虑线程安全、并发访问等问题,并在代码中正确处理异常。例如,在子线程处理任务时,可能需要使用锁或其他同步机制来保护共享资源的访问;在主线程等待子线程执行完毕时,可能需要处理 InterruptedException 异常等异常情况。


http://www.kler.cn/a/6256.html

相关文章:

  • 【Android】application@label 属性属性冲突报错
  • acitvemq AMQP:因为消息映射策略配置导致的MQTT接收JMS消息乱码问题 x-opt-jms-dest x-opt-jms-msg-type
  • Redis--如何保障缓存数据库一致性?(面试高频问题)
  • 某些iphone手机录音获取流stream延迟问题 以及 录音一次第二次不录音问题
  • C++(7)—inline和nullptr
  • 最新高性能多目标优化算法:多目标麋鹿优化算法(MOEHO)求解LRMOP1-LRMOP6及工程应用---盘式制动器设计,提供完整MATLAB代码
  • c# 第一次作业
  • IU5706E低静态电流,同步升压直流-直流控制器
  • CSDN——Markdown编辑器——官方指导
  • 如果觉得上面太正式,请看小红书风格的“从数字化转型到智能化转型”
  • 二叉树全分析(超详细总结建议收藏)
  • ServletAPI详解(二)-HttpServlet类
  • 《SRE实战》实践
  • Linux命令·iostat
  • WSPD:平面最近邻+t-spanner+近似欧氏距离MST(程设实习)
  • 搭建Git服务器-Git钩子的使用
  • 进化吧Java接口兽
  • 代码生成- 引言
  • 编译报错pcl_conversions、及pcl_rosConfig解决方法
  • 位图和布隆过滤器
  • 4.3---Spring框架之Spring中bean的注入方式---(深入版本)
  • 免费CDN-CloudFlare的使用教程
  • STM32个人笔记-I2S
  • Git操作之 git add 撤销、git commit 撤销
  • 原神服务器架设教程(服务器命令代码)
  • C++相关面试题总结一——内存、关键字、STL、指针、排序、Lambda