java并发工具类-CountDownLatch

CountDownLatch的简介
CountDownLatch是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程执行完后再执行。CountDownLatch是在java1.5被引入的,跟它一起被引入的并发工具类还有CyclicBarrier、Semaphore、 ConcurrentHashMap和BlockingQueue,它们都存在于JUC (java.util.concurrent)包下
CountDownLatch原理
CountDownLatch是通过一个计数器来实现的,计数器的初始化值为线程的数量。每当一个线程完成了自己的任务后,计数器的值就相应得减1(CountDownLatch.countDown()
方法)。当计数器到达0时,表示所有的线程都已完成任务,,然后在闭锁上等待CountDownLatch.await()
方法的线程就可以恢复执行任务。
注意:这是一个一次性操作 - 计数无法重置。 如果你需要一个重置的版本计数,考虑使用CyclicBarrier。
应用场景
实现最大的并行性:有时我们想同时启动多个线程,实现最大程度的并行性。例如,我们想测试一个单例类。如果我们创建一个初始计数为1的CountDownLatch,并让所有线程都在这个锁上等待,那么我们可以很轻松地完成测试。我们只需调用 一次countDown()方法就可以让所有的等待线程同时恢复执行。
开始执行前等待n个线程完成各自任务:例如应用程序启动类要确保在处理用户请求前,所有N个外部系统已经启动和运行了,例如处理excel中多个表单。

注意:一个线程不一定只能做countDown一次,也可以countDown多次
CountDownLatch的示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| public class CountDownLatchTest { private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 100, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5));
private static Random random = new Random();
public static void execute(CountDownLatch countDownLatch) { long sleepTime = random.nextInt(10); long threadId = Thread.currentThread().getId(); System.out.println("线程ID" + threadId + ",开始执行--countDown");
try { Thread.sleep(sleepTime * 1000); } catch (InterruptedException e) { e.printStackTrace(); } countDownLatch.countDown(); System.out.println("线程ID" + threadId + ",准备任务完成耗时:" + sleepTime + "当前时间" + System.currentTimeMillis()); try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("线程ID" + threadId + ",开始执行任务,当前时间:" + System.currentTimeMillis()); }
public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(5); for (int i = 0; i < 5; i++) { threadPool.submit(() -> { execute(countDownLatch); }); } countDownLatch.await(); Thread.sleep(1000); threadPool.shutdown(); System.out.println("全部任务执行完成"); } }
|
打印结果
线程ID13,开始执行–countDown
线程ID16,开始执行–countDown
线程ID15,开始执行–countDown
线程ID12,开始执行–countDown
线程ID14,开始执行–countDown
线程ID14,准备任务完成耗时:3当前时间1565159118048
线程ID16,准备任务完成耗时:4当前时间1565159119047
线程ID12,准备任务完成耗时:4当前时间1565159119048
线程ID15,准备任务完成耗时:6当前时间1565159121047
线程ID13,准备任务完成耗时:7当前时间1565159122048
线程ID13,开始执行任务,当前时间:1565159122048
线程ID14,开始执行任务,当前时间:1565159122048
线程ID12,开始执行任务,当前时间:1565159122048
线程ID16,开始执行任务,当前时间:1565159122048
线程ID15,开始执行任务,当前时间:1565159122049
全部任务执行完成
使用CountDownLatch压测

在实战项目中,我们除了使用 jemter 等工具进行压测外,还可以自己动手使用 CountDownLatch 类编写压测代码。可以说 jemter 的并发压测背后也是使用的 CountDownLatch。可见掌握 CountDownLatch 类的使用是有多么的重要。
CountDownLatch是Java多线程同步器的四大金刚之一,CountDownLatch能够使一个线程等待其他线程完成各自的工作后再执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| private void latchTest() throws InterruptedException { int testThreads = 300; final CountDownLatch start = new CountDownLatch(1); final CountDownLatch end = new CountDownLatch(testThreads); ExecutorService exce = Executors.newFixedThreadPool(testThreads); for (int i = 0; i < testThreads; i++) { exce.submit(() -> { try { start.await(); testLoad(); } catch (InterruptedException e) { e.printStackTrace(); } finally { end.countDown(); } });
} start.countDown(); end.await(); exce.shutdown(); }
|
简简单单的几行代码就可以实现300的压测。