[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. 使用 ExecutorService
和 Future
#
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()
获取每个任务的结果。
总结#
Thread.join()
:简单、直接地等待每个线程完成,但需要逐个调用。CountDownLatch
:适合在多个线程完成任务后触发主线程的后续操作。CyclicBarrier
:适合需要多线程相互等待的场景。ExecutorService
和Future
:线程池管理,并行执行任务并等待所有任务完成,适用于多任务并行执行场景。
你可以根据应用的具体需求选择合适的并行等待机制。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步