CountDownLatch、CyclicBarrier和Semaphore
除了使用wait、notify等实现并发线程同步外,jdk还提供了CountDownLatch、CyclicBarrier和Semaphore三个帮助类。
CountDownLatch
- 介绍:实现1个任务等待其他任务完毕之后才执行
- 常用方法:countDown()和await()。通常两个方法需要配合使用。只有当countDown中count为0后,才会继续执行await()方法后续操作。
使用示例(转自):
ublic class Test {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(2);
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
new Thread(){
public void run() {
try {
System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
Thread.sleep(3000);
System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
};
}.start();
try {
System.out.println("等待2个子线程执行完毕...");
latch.await();
System.out.println("2个子线程已经执行完毕");
System.out.println("继续执行主线程");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
CyclicBarrier
- CyclicBarrier是让一组线程等待某一个状态后全部同时执行,
- 常用方法:public int await()。挂起线程等待
- CyclicBarrier barrier = new CyclicBarrier(N); 有N个线程调用await(),才会继续执行后续操作
示例:
public class Test {
public static void main(String[] args) {
int N = 4;
CyclicBarrier barrier = new CyclicBarrier(N);
for(int i=0;i<N;i++)
new Writer(barrier).start();
}
static class Writer extends Thread{
private CyclicBarrier cyclicBarrier;
public Writer(CyclicBarrier cyclicBarrier) {
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
System.out.println("线程"+Thread.currentThread().getName()+"正在写入数据...");
try {
Thread.sleep(5000); //以睡眠来模拟写入数据操作
System.out.println("线程"+Thread.currentThread().getName()+"写入数据完毕,等待其他线程写入完毕");
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
}catch(BrokenBarrierException e){
e.printStackTrace();
}
System.out.println("所有线程写入完毕,继续处理其他任务...");
}
}
}
CountDownLatch和CyclicBarrier使用区别
1 CountDownLatch的计数器只能使用一次,而CyclicBarrier的计数器可以使用reset()方法重
置。所以CyclicBarrier能处理更为复杂的业务场景。例如,如果计算发生错误,可以重置计数
器,并让线程重新执行一次。
2 CyclicBarrier还提供其他有用的方法,比如getNumberWaiting方法可以获得Cyclic-Barrier
阻塞的线程数量。isBroken()方法用来了解阻塞的线程是否被中断。
CountDownLatch这个类能够使一个线程等待其他线程完成各自的工作后再执行。例如,应用程序的主线程希望在负责启动框架服务的线程已经启动所有的框架服务之后再执行。
CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就会减1。当计数器值到达0时,它表示所有的线程已经完成了任务,然后在闭锁上等待的线程就可以恢复执行任务
countdownlatch 一个线程等所有其他线程完成任务,这个线程在继续运行。一个等待多个
cyclicbarrier是一组线程相互等待,到点以后所有现场再运行,场景:压测集合点。多个等待其他多个
Semaphore
- Semaphore信号量,是一种基于计数的信号量。
- 先设定阈值,多个线程获取信号量,当被获取的信号量超过阈值时,将等待。
使用示例
// 只能5个线程同时访问
Semaphore semp = new Semaphore(5);
try {
// 申请许可
semp.acquire();
try {
// 业务逻辑
} catch (Exception e) {
} finally {
// 释放许可
semp.release();
}
} catch (InterruptedException e) {
}
有点类似线程池的思想,比如创建5个线程的线程池,当超过5个,后续加进来的线程就会等待。
- 和ReentrantLock一样,Semaphore也有公平性和非公平性(默认)两种模式。