摘要
CountDownLatch、Semaphore和CyclicBarrier都是用于同步一批线程的行为
CountDownLatch
是一个计数器闭锁,同时只能有一个线程去操作该计数器,通过它可以完成类似于阻塞当前线程的功能。
方法说明:
- await()
线程会一直处于阻塞状态,当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行 - countDown()
计数器减一。这种现象只会出现一次,因为计数器不能被重置,如果业务上需要一个可以重置计数次数的版本,可以考虑使用CycliBarrier
通过CountDownLatch 可以模拟并发场景
static int THREAD_NUM=1000;
static CountDownLatch countDownLatch=new CountDownLatch(THREAD_NUM);
for(int i=0;i<THREAD_NUM;i++){
final int id=i;
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
try{
countDownLatch.await();
//每次请求都直接通过service获取数据
ReqMerger reqMerger =reqMergerService.queryById(id);
}catch (Exception ex){
System.out.println(ex.getMessage());
}
}
});
thread.start();
countDownLatch.countDown();
}
Semaphore
Semaphore的值被获取到后是可以释放的,不像CountDownLatch那样一直减到底。它也被更多地用来限制流量,类似阀门的功能。如果限定某些资源最多有N个线程可以访问,那么超过N个就不允许再有线程来访问,同时当现有线程结束后,就会释放,然后允许新的线程进来。有点类似于锁的lock与 unlock过程。
方法说明:
- acquire()
用于获取权限,其底层实现与CountDownLatch.countdown()类似 - release()
用于释放权限
private final static int threadCount = 20;
static ExecutorService exec = Executors.newCachedThreadPool();
public static void main(String[] args){
// 每次最多三个线程获取许可
final Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
// 获取一个许可
semaphore.acquire();
test(threadNum);
// 释放一个许可
semaphore.release();
} catch (Exception e) {
System.out.println(e.getMessage());
}
});
}
exec.shutdown();
}
private static void test(int threadNum) throws Exception {
System.out.println(threadNum);
Thread.sleep(1000);
}
CyclicBarrier
允许一组线程相互等待,直到到达某个公共屏障点。通过它可以完成多个线程之间相互等待,只有当每个线程都准备就绪后,才能各自继续往下执行后面的操作。当某个线程调用await方法时,该线程进入等待状态,且计数器加1,当计数器的值达到设置的初始值时,所有因调用await进入等待状态的线程被唤醒,继续执行后续操作。因为CycliBarrier在释放等待线程后可以重用,所以称为循环barrier。CycliBarrier支持一个可选的Runnable,在计数器的值到达设定值后(但在释放所有线程之前),该Runnable运行一次,注,Runnable在每个屏障点只运行一个。
private static CyclicBarrier barrier = new CyclicBarrier(5);
static ExecutorService executor = Executors.newCachedThreadPool();
public static void main(String[] args) throws Exception {
for (int i = 0; i < 10; i++) {
final int threadNum = i;
Thread.sleep(1000);
executor.execute(() -> {
try {
test(threadNum);
} catch (Exception e) {
System.out.println(e);
}
});
}
executor.shutdown();
}
private static void test(int threadNum) throws Exception {
Thread.sleep(1000);
System.out.println(threadNum+" is ready");
barrier.await();
System.out.println(threadNum+" is continue");
}
运行结果
0 is ready
1 is ready
2 is ready
3 is ready
4 is ready
4 is continue
1 is continue
0 is continue
3 is continue
2 is continue
5 is ready
6 is ready
7 is ready
8 is ready
9 is ready
5 is continue
9 is continue
6 is continue
7 is continue
8 is continue