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

在 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:线程池管理,并行执行任务并等待所有任务完成,适用于多任务并行执行场景。

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

作者:Esofar

出处:https://www.cnblogs.com/DCFV/p/18438039

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Duancf  阅读(214)  评论(0编辑  收藏  举报
努力加载评论中...
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示