
在JDK的并发包里提供了几个非常有用的并发工具类。如线程等待的CountDownLatch和CyclicBarrier。
二、等待多线程完成的CountDownLatchCountDownLatch允许一个或多个线程等待其他线程完成操作后再执行后续的代码。
2.1 应用场景countDown(): 将CountDownLatch对象内部维护的state变量减1。
wait():等待CountDownLatch对象内部维护的state变量减为0之后再往下执行代码。
public void getShellData(){
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.MINUTES, new ArrayBlockingQueue<>(5));
CountDownLatch latch = new CountDownLatch(5);
List shellDataList = new ArrayList<>();
for (int i = 0; i < 5; i++) {
int finalI = i;
threadPoolExecutor.execute(()->{
shellDataList.add(getShellData(finalI));
latch.countDown();
});
}
latch.wait();
// 5个shell页的数据都获取完了,再对汇总后的数据进行处理
doOtherByShellDataList(shellDataList);
}
2.3 死锁问题
如果线程池中线程的数量较少,在高并发时会出现多个请求占用了全部的线程,但是每个请求又需要await其他线程,被等待的线程拿不到线程资源无法执行,导致多个请求同时进入线程阻塞,最后形成死锁。
2.3.1 解决方式使用自定义线程池,请求队列控制数量,等待加上超时时间,使用拒绝策略。
三、同步屏障CyclicBarrierCyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让所有的线程都到达屏障(同步点)的时候,屏障才会打开,所有的线程才会往下执行。
3.1 应用场景await():将当前线程阻塞,等到所有的线程都到达屏障后一起执行。
reset():将屏障重置为初始状态之后就可以复用。
isBroken(): 方法检测Barrier是否被破坏。
public static void main(String[] args) throws BrokenBarrierException, InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.MINUTES, new ArrayBlockingQueue<>(5));
CyclicBarrier barrier = new CyclicBarrier(5);
for (int i = 0; i < 5; i++) {
threadPoolExecutor.execute(()->{
try {
Thread.sleep(Math.round(1000));
System.out.println(Thread.currentThread().getName() + ": 玩家准备完成。");
barrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
});
}
barrier.await();
System.out.println("玩家全部准备完成!开始游戏。");
threadPoolExecutor.shutdown();
}
pool-1-thread-3: 玩家准备完成。
pool-1-thread-1: 玩家准备完成。
pool-1-thread-2: 玩家准备完成。
pool-1-thread-4: 玩家准备完成。
pool-1-thread-5: 玩家准备完成。
玩家全部准备完成!开始游戏。
四、CountDownLatch与CyclicBarrier的区别