CountDownLatch与CyclicBarrier使用及区别
CountDownLatch与CyclicBarrier使用及区别
- 一、`CountDownLatch`
- **主要用途**
- **核心方法**
- **使用示例**
- 示例 1:主线程等待多个子线程完成任务
- 示例 2:多个子线程等待主线程发出信号
- **注意事项**
- 二、`CyclicBarrier`
- **主要用途**
- **核心方法**
- **使用示例**
- 示例 1:多个线程相互等待
- 示例 2:分阶段任务
- **注意事项**
- **`CyclicBarrier`与 `CountDownLatch` 的区别**
- **适用场景**
一、CountDownLatch
java.util.concurrent.CountDownLatch
是 Java 并发编程中的一个同步工具类,用于让一个或多个线程等待其他线程完成操作后再继续执行。它的核心思想是通过一个计数器来实现线程的等待和唤醒。
主要用途
CountDownLatch
通常用于以下场景:
- 主线程等待多个子线程完成任务:
- 例如,主线程需要等待多个子线程初始化完成后才能继续执行。
- 多个子线程等待主线程发出信号:
- 例如,多个子线程需要等待主线程发出“开始”信号后才能同时执行。
核心方法
CountDownLatch(int count)
:- 构造函数,初始化计数器。
count
是需要等待的操作次数。
- 构造函数,初始化计数器。
void await()
:- 使当前线程等待,直到计数器减到 0。
boolean await(long timeout, TimeUnit unit)
:- 使当前线程等待,直到计数器减到 0 或超时。
void countDown()
:- 将计数器减 1,表示一个操作已完成。
long getCount()
:- 返回当前计数器的值。
使用示例
示例 1:主线程等待多个子线程完成任务
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample {
public static void main(String[] args) throws InterruptedException {
int threadCount = 5;
CountDownLatch latch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 完成任务");
latch.countDown(); // 计数器减 1
}).start();
}
latch.await(); // 主线程等待所有子线程完成任务
System.out.println("所有任务已完成,主线程继续执行");
}
}
输出:
Thread-0 完成任务
Thread-1 完成任务
Thread-2 完成任务
Thread-3 完成任务
Thread-4 完成任务
所有任务已完成,主线程继续执行
示例 2:多个子线程等待主线程发出信号
import java.util.concurrent.CountDownLatch;
public class CountDownLatchExample2 {
public static void main(String[] args) throws InterruptedException {
int threadCount = 5;
CountDownLatch startSignal = new CountDownLatch(1);
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
startSignal.await(); // 子线程等待主线程的信号
System.out.println(Thread.currentThread().getName() + " 开始执行");
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
Thread.sleep(2000); // 模拟主线程准备时间
System.out.println("主线程发出开始信号");
startSignal.countDown(); // 主线程发出信号
}
}
输出:
主线程发出开始信号
Thread-0 开始执行
Thread-1 开始执行
Thread-2 开始执行
Thread-3 开始执行
Thread-4 开始执行
注意事项
- 计数器不可重置:
CountDownLatch
的计数器只能减不能增,一旦计数器减到 0,就不能再使用了。如果需要重复使用,可以考虑CyclicBarrier
。
- 避免死锁:
- 确保所有线程都会调用
countDown()
,否则主线程会一直等待。
- 确保所有线程都会调用
- 性能开销:
CountDownLatch
是基于 AQS(AbstractQueuedSynchronizer)实现的,性能较高,适合高并发场景。
二、CyclicBarrier
java.util.concurrent.CyclicBarrier
是 Java 并发编程中的一个同步工具类,用于让一组线程相互等待,直到所有线程都到达某个屏障点(Barrier Point)后再继续执行。与 CountDownLatch
不同,CyclicBarrier
是可以重复使用的。
主要用途
CyclicBarrier
通常用于以下场景:
- 多线程任务分阶段执行:
- 例如,多个线程需要先完成第一阶段的任务,然后同时开始第二阶段的任务。
- 并行计算:
- 例如,将一个大型计算任务拆分为多个子任务,每个子任务由一个线程处理,所有线程完成后合并结果。
核心方法
CyclicBarrier(int parties)
:- 构造函数,初始化屏障,
parties
是需要等待的线程数。
- 构造函数,初始化屏障,
CyclicBarrier(int parties, Runnable barrierAction)
:- 构造函数,初始化屏障,并指定所有线程到达屏障后执行的操作(
barrierAction
)。
- 构造函数,初始化屏障,并指定所有线程到达屏障后执行的操作(
int await()
:- 使当前线程等待,直到所有线程都到达屏障。
int await(long timeout, TimeUnit unit)
:- 使当前线程等待,直到所有线程都到达屏障或超时。
void reset()
:- 重置屏障,使其可以重复使用。
使用示例
示例 1:多个线程相互等待
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample {
public static void main(String[] args) {
int threadCount = 3;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("所有线程已到达屏障,继续执行");
});
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 到达屏障");
barrier.await(); // 等待其他线程
System.out.println(Thread.currentThread().getName() + " 继续执行");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
输出:
Thread-0 到达屏障
Thread-1 到达屏障
Thread-2 到达屏障
所有线程已到达屏障,继续执行
Thread-2 继续执行
Thread-0 继续执行
Thread-1 继续执行
示例 2:分阶段任务
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierExample2 {
public static void main(String[] args) {
int threadCount = 3;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println("当前阶段完成,进入下一阶段");
});
for (int i = 0; i < threadCount; i++) {
new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName() + " 完成阶段 1");
barrier.await(); // 等待其他线程完成阶段 1
System.out.println(Thread.currentThread().getName() + " 完成阶段 2");
barrier.await(); // 等待其他线程完成阶段 2
System.out.println(Thread.currentThread().getName() + " 完成阶段 3");
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
}
}
输出:
Thread-0 完成阶段 1
Thread-1 完成阶段 1
Thread-2 完成阶段 1
当前阶段完成,进入下一阶段
Thread-2 完成阶段 2
Thread-0 完成阶段 2
Thread-1 完成阶段 2
当前阶段完成,进入下一阶段
Thread-1 完成阶段 3
Thread-0 完成阶段 3
Thread-2 完成阶段 3
注意事项
- 屏障点操作:
- 如果指定了
barrierAction
,它会在最后一个线程到达屏障后执行,然后所有线程才会继续执行。
- 如果指定了
- 异常处理:
- 如果某个线程在等待时被中断或超时,屏障会被破坏,所有等待的线程会抛出
BrokenBarrierException
。
- 如果某个线程在等待时被中断或超时,屏障会被破坏,所有等待的线程会抛出
- 重复使用:
CyclicBarrier
可以重复使用,每次所有线程到达屏障后,计数器会自动重置。
CyclicBarrier
与 CountDownLatch
的区别
特性 | CyclicBarrier | CountDownLatch |
---|---|---|
计数器重置 | 可重置 | 不可重置 |
用途 | 多个线程相互等待 | 一个线程等待多个线程完成 |
触发条件 | 所有线程到达屏障点 | 计数器减到 0 |
重复使用 | 支持 | 不支持 |
适用场景
- 如果任务需要分阶段执行,并且每个阶段需要所有线程同步,使用
CyclicBarrier
。 - 如果只需要一个线程等待其他线程完成,使用
CountDownLatch
。