背景
做实验过程中,想统计某个接口在并发请求的情况下的平均响应时间,如何用Java去实现这个功能呢?
技术实现
一、可能遇到的问题
1、我们可以利用多线程去实现,只要多开几个线程,发起请求就好了,但是这不是真正的并发!因为线程的创建是有先后顺序的,这样做本质还是先创建的线程先执行。
2、操作系统对线程的调度我们是不知道的,我们该如何实现,主线程等待所有子线程执行完毕后,才去统计子线程耗时呢?
这里就要用到Java中的CountDownLatch类。
二、什么是countDownlatch
CountDownLatch是一个同步工具类,它通过一个计数器来实现的,初始值为线程的数量。每当一个线程完成了自己的任务,计数器的值就相应得减1。当计数器到达0时,表示所有的线程都已执行完毕,然后在等待的线程就可以恢复执行任务。
关键方法:
countDown(): 每调用一次计数器值-1,直到count被减为0,代表所有线程全部执行完毕。
await(): 等待计数器变为0,即等待所有异步线程执行完毕。
二、利用countDownlatch实现多个线程同时启动
1、实现自定义任务线程
public class MyConcurrentThreadHcode implements Callable<Long> {private final CountDownLatch latch;private final CountDownLatch latchTotal;public MyConcurrentThreadHcode(CountDownLatch latch, CountDownLatch latchTotal) {this.latch = latch;this.latchTotal = latchTotal;}public Long call() {System.out.println(Thread.currentThread().getName()+",prepare at: "+System.currentTimeMillis());try {//先阻塞在这里,等待主线程将latch的计数器减为0latch.await();return doTask();} catch (InterruptedException e) {e.printStackTrace();} finally {//执行完一次latchTotal的计数器减一,latchTotal减到0后,主线程才会继续往下执行latchTotal.countDown();}return -1L;}public long doTask() {Map<String, Object> params = new HashMap<String, Object>(7);params.put("minX", -68.8);params.put("maxX", -65);params.put("minY", 17);params.put("maxY", 20);params.put("startTimeStr", "2022-01-03T00:00:00");params.put("endTimeStr", "2022-01-03T10:00:00");long startTime = System.currentTimeMillis();String result = HttpUtil.get("http://localhost:6068/test/QueryByHCode", params);long endTime = System.currentTimeMillis();return endTime - startTime;}
}
2、主线程创建多线程,并同时启动
public static void testConcurrentWCodeQuery(int totalThreadNum) throws ExecutionException, InterruptedException {//01 创建指定threadNum个数的线程CountDownLatch latchTotal = new CountDownLatch(totalThreadNum);CountDownLatch latch = new CountDownLatch(1);List<FutureTask> tasks = new ArrayList<>(totalThreadNum);for (int i = 0; i < totalThreadNum ; i++) {MyConcurrentThreadWcode task = new MyConcurrentThreadWcode(latch, latchTotal);FutureTask futureTask = new FutureTask(task);tasks.add(futureTask);Thread thread = new Thread(futureTask);thread.start();}//同时启动多个线程latch.countDown();try {/**所有子线程未执行完,主线程会阻塞在这里*/latchTotal.await();} catch (InterruptedException e) {e.printStackTrace();}long totalTime = 0L;int num = 0;for(FutureTask task : tasks){Long time = (Long)task.get();if(time > 0L){totalTime += time;num ++;}}System.out.println("查询平均耗时:" + totalTime/num + "ms");}
在主线程和任务线程中,创建了两个CountDownLatch变量,一个变量latch的初始计数器为1,变量latchTotal的初始计数器为线程数。
latch变量的作用是在主线程创建完所有子线程后,再启动子线程。
latchTotal变量的作用是在子线程执行完所有任务后,在执行主线程代码。
如此,便能实现多个线程同时启动,并在所有子线程执行完毕之后,统计其总耗时。