首页 > 编程语言 >[Java并发]线程的并行等待

[Java并发]线程的并行等待

时间:2024-09-28 15:46:15浏览次数:1  
标签:Java Thread System 并发 finished 线程 println out

在 Java 中,线程的并行等待(即等待多个线程并行执行完毕)通常可以通过以下几种方式来实现。我们要确保多个线程能够并行执行,并在所有线程执行完毕后再继续后续的操作。这种场景通常可以用在并发任务的协调中。以下是几种常见的实现方式:

1. 使用 Thread.join() 方法

join() 方法是最直接的方式,用于等待一个线程执行完毕。为了实现并行等待多个线程的完成,可以对每个线程分别调用 join(),主线程会依次等待每个子线程执行结束。

示例:

public class ParallelWaitExample {
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 started");
            try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("Thread 1 finished");
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 started");
            try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("Thread 2 finished");
        });

        thread1.start();
        thread2.start();

        // 主线程等待两个线程完成
        thread1.join(); // 等待 thread1 完成
        thread2.join(); // 等待 thread2 完成

        System.out.println("All threads finished");
    }
}

解释:在这个例子中,主线程依次调用 thread1.join()thread2.join(),确保在两个线程都执行完毕后继续执行后续逻辑。

2. 使用 CountDownLatch

CountDownLatch 是一个同步工具类,可以用来协调多个线程的执行顺序。通过它,主线程可以等待一组线程执行完毕。

使用步骤:

  • 创建 CountDownLatch,初始值为线程数。
  • 每个线程在任务完成后调用 countDown(),表示自己完成任务。
  • 主线程调用 await() 方法,等待计数器归零(即所有线程完成任务)。

示例:

import java.util.concurrent.CountDownLatch;

public class CountDownLatchExample {
    public static void main(String[] args) throws InterruptedException {
        int numThreads = 2;
        CountDownLatch latch = new CountDownLatch(numThreads);

        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 started");
            try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("Thread 1 finished");
            latch.countDown(); // 任务完成,计数器减1
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 started");
            try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("Thread 2 finished");
            latch.countDown(); // 任务完成,计数器减1
        });

        thread1.start();
        thread2.start();

        latch.await(); // 主线程等待所有线程完成
        System.out.println("All threads finished");
    }
}

解释CountDownLatch 的计数器初始值为 2,表示等待两个线程完成。每个线程执行完毕后调用 countDown(),主线程调用 await() 阻塞,直到所有线程完成任务。

3. 使用 CyclicBarrier

CyclicBarrier 是一个同步辅助工具,用来让一组线程等待彼此到达一个共同的屏障点。当指定数量的线程都调用了 await() 方法时,所有线程才会继续执行。

示例:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class CyclicBarrierExample {
    public static void main(String[] args) {
        int numThreads = 2;
        CyclicBarrier barrier = new CyclicBarrier(numThreads, () -> {
            // 所有线程都到达屏障后执行该任务
            System.out.println("All threads finished");
        });

        Thread thread1 = new Thread(() -> {
            System.out.println("Thread 1 started");
            try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("Thread 1 finished");
            try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); }
        });

        Thread thread2 = new Thread(() -> {
            System.out.println("Thread 2 started");
            try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); }
            System.out.println("Thread 2 finished");
            try { barrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); }
        });

        thread1.start();
        thread2.start();
    }
}

解释CyclicBarrier 的构造参数是 2,表示两个线程都需要到达屏障(调用 await())后才能继续执行。所有线程到达后,屏障会触发并执行定义的任务。

4. 使用 ExecutorServiceFuture

ExecutorService 提供了一种管理线程池的方式,它可以提交一组任务并使用 Future 来等待它们的完成。

示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.Callable;
import java.util.ArrayList;
import java.util.List;

public class ExecutorServiceExample {
    public static void main(String[] args) throws Exception {
        ExecutorService executor = Executors.newFixedThreadPool(2);

        List<Callable<String>> tasks = new ArrayList<>();
        tasks.add(() -> {
            System.out.println("Task 1 started");
            Thread.sleep(1000);
            System.out.println("Task 1 finished");
            return "Result 1";
        });
        tasks.add(() -> {
            System.out.println("Task 2 started");
            Thread.sleep(2000);
            System.out.println("Task 2 finished");
            return "Result 2";
        });

        // 提交任务并获取 Future 列表
        List<Future<String>> results = executor.invokeAll(tasks);

        for (Future<String> result : results) {
            System.out.println("Task result: " + result.get()); // 等待任务完成并获取结果
        }

        executor.shutdown();
    }
}

解释:使用 ExecutorService 创建一个线程池,通过 invokeAll() 提交任务,主线程会阻塞直到所有任务执行完毕,然后可以通过 Future.get() 获取每个任务的结果。

总结

  1. Thread.join():简单、直接地等待每个线程完成,但需要逐个调用。
  2. CountDownLatch:适合在多个线程完成任务后触发主线程的后续操作。
  3. CyclicBarrier:适合需要多线程相互等待的场景。
  4. ExecutorServiceFuture:线程池管理,并行执行任务并等待所有任务完成,适用于多任务并行执行场景。

你可以根据应用的具体需求选择合适的并行等待机制。

标签:Java,Thread,System,并发,finished,线程,println,out
From: https://www.cnblogs.com/DCFV/p/18438039

相关文章

  • javascript 数组对象解构
    传统的写法不好记忆,书写麻烦,此时可以使用结构赋值的方法让代码更加简洁。数组结构是将数组中的单元值快速批量赋值给一系列变量的简介语法。变量的顺序对应数组单元值位置一次进行赋值操作。如下:应用一:交换两个变量Js前面有那种情况需要加分号。(不加分号解析器认为和上......
  • DC00020基于springboot新闻网站系统java web项目MySQL新闻管理系统
    1、项目功能演示DC00020基于springboot新闻网站系统javaweb项目MySQL2、项目功能描述  基于springboot+vue新闻网站包括用户和系统管理员两个角色。2.1用户功能1、用户登录、用户注册2、新闻信息:点赞、点踩、收藏、查看3、用户分享:点赞、点踩、收藏、查看、添加......
  • Java lambda表达式
    概述:今天推荐一篇!Java1.8的新特性之lambda表达式,它非常的方便,可以用来简化匿名内部类哦。能够以更清晰、直观的方式表达代码的逻辑。极大的简化了代码的数量。⌨️⌨️⌨️不会匿名内部类的小伙伴可以博主下面这篇文章哦:Java匿名内部类https://blog.csdn.net/ouhexie/article/deta......
  • Linux线程-POSIX信号量与锁以及条件变量
    POSIX信号量POSIX没有元素这个概念相比于SYSTEM-V更简洁,POSIX不一定适用老版本;二者都是系统范畴,都需要手动删除,POSIX相关函数属于线程库,所有编译时需要末尾加上-lpthread选项POSIXPOSIX有名信号量主要用于进程间通信创建成功后,器特殊文件存放路径:/dev/shm/POSIX无名......
  • 2024java社招面试(亲身经历8w字,更新中)
    一、Java基础部分面试题1.Java面向对象的三个特征封装:对象只需要选择性的对外公开一些属性和行为。继承:子对象可以继承父对象的属性和行为,并且可以在其之上进行修改以适合更特殊的场景需求。多态:允许不同类的对象对同一消息做出响应。2.Java中基本的数据类型有哪些以......
  • 最详细JAVA八股文,欢迎收藏
    1、java内存模型JMM    JMM是一个抽象的规范,它定义了共享内存中多线程程序读写规范,即变量如何存储在内存中又如何从内存中读取(这里的变量指实例字段,静态变量,构成数组的元素)JMM将内存分为主内存和工作内存主内存是线程共享的公共区域,里面存储了共享变量工作内存是线......
  • 全网最新Java面试八股文题
    1、ThreadPoolExecutor对象有哪些参数怎么设定核心线程数和最大线程数拒绝策略有哪些  难度系数:⭐corePoolSize:核心线程池的大小maximumPoolSize:线程池能创建线程的最大个数keepAliveTime:空闲线程存活时间unit:时间单位,为keepAliveTime指定时间单位workQueue:阻塞队......
  • java毕业设计-基于springboot+vue的茶文化社区与交易平台小程序设计和实现,基于springb
    博主介绍:✌️码农一枚,专注于大学生项目实战开发、讲解和毕业......
  • java毕业设计-基于springboot+vue的大学生社团信息化管理平台设计和实现,基于springboo
    博主介绍:✌️码农一枚,专注于大学生项目实战开发、讲解和毕业......
  • Linux多线程复制目录
    比如有一个目录a需要复制:mkdiratoucha/amkdira/btoucha/b/ctreeaa├──a└──b└──c将其复制为目录d只需要用find找出目录下所有的文件,然后用gnu-parallel开多线程逐个复制即可:mkdirdcdafind.-typef|parallel-j4cp--parents{}../dtre......