juc包下有个类叫CountDownLatch
CountDownLatch不是锁,主要作用是使一个线程等待其他线程各自执行完毕后再执行,通过计数器来实现的。
计数器是无法重置的,当计数器被减到0时,调用await方法都会直接返回。
执行countDown方法的线程不会进行阻塞,执行awit方法的线程才会阻塞。同时也可以设置等待过期时间,等待时间过后开始执行。
场景一:普通场景
public static void main(String[] args) {
System.out.println("start latch");
Thread[] threads = new Thread[100];
// CountDownLatch在初始化时,计数器初始值就是线程的数量。
CountDownLatch latch = new CountDownLatch(threads.length);
for(int i = 0;i<threads.length;i++){
threads[i] = new Thread(()->{
int result = 0;
for (int j = 0; j < 10000; j++) {
result+=j;
}
// 当调用countDown方法时,计数器会被减1;
latch.countDown();
});
}
for (int i = 0; i < threads.length; i++) {
threads[i].start();
}
System.out.println("计数器值:"+latch.getCount());
try{
// 当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒,否则一直阻塞
// 典型生活场景比如:打麻将,只有4人都到时,打麻将主线程才会启动
latch.await();
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println("end latch");
}
超出指定等待时间也会直接返回
// 使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
boolean await(long timeout, TimeUnit unit)
场景二:配合使用信号量可以做一个定时任务
线程池可以使用ExecutorService直接new一个固定数量的线程池
// 从接口读取一批数据
List<Data> list = apiUtils.getList();
// 通过计数器实现CountDownLatch,参数为这批数据的大小
CountDownLatch latch = new CountDownLatch(list.size());
// for循环执行这批数据,循环体内用线程池处理每条数据,每条数据处理完countDown
for(Object data:list){
线程池.execute(new JobTask(latch,list)
}
// 最后await释放计数器CountDownLatch
latch.await()
class JobTask implements Runnable{
CountDownLatch countDownLatch;
Object data;
public JobTask(CountDownLatch latch,Object data){
this.countDownLatch = latch;
this.data = data;
}
@Override
public void run(){
doSomething();
countDownLatch.countDown();
}
}