Java基础知识12--使用CountDownLatch实现模拟多线程并发请求


Java中模拟并发请求,自然是很方便的,只要多开几个线程,发起请求就好了。但是,这种请求,一般会存在启动的先后顺序了,算不得真正的同时并发!怎么样才能做到真正的同时并发呢?是本文想说的点,java中提供了闭锁 CountDownLatch, 刚好就用来做这种事就最合适了。

只需要:

1. 开启n个线程,加一个闭锁,开启所有线程;

2. 待所有线程都准备好后,按下开启按钮,就可以真正的发起并发请求了。

1.CountDownLatch

CountDownLatch 是 java.util.concurrent 包下的一个同步辅助类,它能使一个或多个线程在其他的线程的一系列操作完成之前一直等待。

CountDownLatch是一种通用的同步工具,可用于多种用途。

(1) 一个CountDownLatch为一个计数的CountDownLatch用作一个简单的开/关锁存器,或者门:所有线程调用await在门口等待,直到被调用countDown()的线程打开。

(2) 一个CountDownLatch初始化N可以用来做一个线程等待,直到N个线程完成某项操作,或某些动作已经完成N次

CountDownLatch 典型应用:

  1. 某一线程在开始运行前等待n个线程执行完毕。将CountDownLatch的计数器初始化为new CountDownLatch(n),每当一个任务线程执行完毕,就将计数器减1 countdownLatch.countDown(),当计数器的值变为0时,在CountDownLatch上await()的线程就会被唤醒。一个典型应用场景就是启动一个服务时,主线程需要等待多个组件加载完毕,之后再继续执行。
  2. CountDownLatch典型用法:2、实现多个线程开始执行任务的最大并行性。注意是并行性,不是并发,强调的是多个线程在某一时刻同时开始执行。类似于赛跑,将多个线程放到起点,等待发令枪响,然后同时开跑。做法是初始化一个共享的CountDownLatch(1),将其计算器初始化为1,多个线程在开始执行任务前首先countdownlatch.await(),当主线程调用countDown()时,计数器变为0,多个线程同时被唤醒

常用方法API

(1)await()方法

public void await() throws InterruptedException

线程阻塞,直到计数器为0,才会启动。

(2)countDown()方法

public void countDown()

减少锁存器的计数,如果计数达到零,释放所有等待的线程。

(3)构造方法

public CountDownLatch(int count)

构造一个以给定计数 CountDownLatch CountDownLatch。

2.使用CountDownLatch实现模拟多线程并发请求

测试线程类:

package org.jeecg.modules.test;

import java.util.Random;
import java.util.concurrent.CountDownLatch;

/**
 * @Author lucky
 * @Date 2021/11/18 14:48
 */
public class MyTestThread implements Runnable{
    private final CountDownLatch startSignal;

    public MyTestThread(CountDownLatch startSignal) {
        super();
        this.startSignal = startSignal;
    }


    @Override
    public void run() {
        try {
            startSignal.await(); //一直阻塞当前线程,直到计时器的值为0
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        doTask();
    }

    private void doTask() {
        // TODO Auto-generated method stub
        long startTime = System.currentTimeMillis();
        //1. 模拟调用Flep SDK请求操作(让线程睡眠随机毫秒数)
        Random random = new Random();
        int timeduration=100+random.nextInt(300); // 返回给定返回的随机秒数
        try {
            Thread.sleep(timeduration);
        } catch (Exception e) {
            e.printStackTrace();
        }
        long endTime = System.currentTimeMillis();
        System.out.println(Thread.currentThread().getName() + " ended at: " + endTime + ", cost: " + (endTime - startTime) + " ms.");
    }

}

测试主函数类 

package org.jeecg.modules.test;

import java.util.concurrent.CountDownLatch;

/**
 * @Author lucky
 * @Date 2021/11/18 15:04
 */
public class MyMainTest {

    public void concurrentInit(int threadNum){
        CountDownLatch start=new CountDownLatch(1);// 初始化计数器为1
        for(int i=0;i//模拟用户设置的给定数量的线程
            MyTestThread tt =new MyTestThread(start);
            Thread t = new Thread(tt);
            t.start();
        }
        start.countDown();//计数器減1  所有线程释放 同时跑
    }

    public static void main(String[] args) {
        int threadNum=20;
        MyMainTest myMainTest=new MyMainTest();
        myMainTest.concurrentInit(threadNum);

    }
}

参考文献:https://blog.csdn.net/u010739551/article/details/79538425

https://blog.csdn.net/qq_45537574/article/details/111910615 (CountDownLatch+ThreadPoolExecutor线程池)

https://blog.csdn.net/linzhijia0612/article/details/44017715

https://blog.csdn.net/qq_38955098/article/details/109386827