java代码模拟并发请求

淩亂°似流年 2023-06-30 11:59 97阅读 0赞

转载:https://www.cnblogs.com/yougewe/p/9745198.html

java中如何模拟真正的同时并发请求?

有时需要测试一下某个功能的并发性能,又不要想借助于其他工具,【试过用postman工具,delay为0貌似还是串行,并非是并发】索性就自己的开发语言,来一个并发请求就最方便了。

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

只需要:

  1. 开启n个线程,加一个闭锁,开启所有线程;
  2. 待所有线程都准备好后,按下开启按钮,就可以真正的发起并发请求了。
    如BingFaTest.java Junit测试

    package com.launch.sharedevice;

    import java.util.HashMap;
    import java.util.Map;
    import java.util.concurrent.CountDownLatch;

    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.http.HttpEntity;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import org.springframework.test.context.web.WebAppConfiguration;
    import org.springframework.util.MultiValueMap;
    import org.springframework.web.client.RestTemplate;
    import andy.config.RestTemplateConfig;

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations={ “classpath:spring.xml”,”classpath:spring-mybatis.xml”,”classpath:spring-rabbitmq.xml”})
    @WebAppConfiguration
    public class BingFaTest {

    1. private Logger logger = LoggerFactory.getLogger(this.getClass());
    2. @Autowired
    3. private RestTemplate restTemplate;
    4. @Test
    5. public void everTest() throws InterruptedException {
    6. Runnable taskTemp = new Runnable() {
    7. // 注意,此处是非线程安全的,留坑
    8. private int iCounter;
    9. @Override
    10. public void run() {
    11. for(int i = 0; i < 10; i++) {
    12. // --------------发起请求--------------------
    13. // url为项目中的某个接口
    14. String url = "http://localhost:9080/repair/device/close";
    15. Map<String, Object> map = new HashMap<>();
    16. map.put("mechanicCode", "590026109");
    17. map.put("deviceNo", "980000000000");
    18. HttpEntity<MultiValueMap<String, Object>> paramrequest = RestTemplateConfig.getFormDataHttpEntity(map);
    19. String result = restTemplate.postForObject(url, paramrequest, String.class);
    20. logger.info("---------result>:"+result);
    21. // --------------请求结束--------------------
    22. iCounter++;
    23. System.out.println(System.nanoTime() + " runnable[" + Thread.currentThread().getName() + "] iCounter = " + iCounter);
    24. try {
    25. Thread.sleep(100);
    26. } catch (InterruptedException e) {
    27. e.printStackTrace();
    28. }
    29. }
    30. }
    31. };
    32. long total = startTaskAllInOnce(5, taskTemp);
    33. System.out.println("----消耗时间为>:"+total);
    34. }
    35. public long startTaskAllInOnce(int threadNums, final Runnable task) throws InterruptedException {
    36. final CountDownLatch startGate = new CountDownLatch(1);
    37. final CountDownLatch endGate = new CountDownLatch(threadNums);
    38. for(int i = 0; i < threadNums; i++) {
    39. Thread t = new Thread() {
    40. public void run() {
    41. try {
    42. // 使线程在此等待,当开始门打开时,一起涌入门中
    43. startGate.await();
    44. try {
    45. task.run();
    46. } finally {
    47. // 将结束门减1,减到0时,就可以开启结束门了
    48. endGate.countDown();
    49. }
    50. } catch (InterruptedException ie) {
    51. ie.printStackTrace();
    52. }
    53. }
    54. };
    55. t.start();
    56. }
    57. long startTime = System.nanoTime();
    58. System.out.println(startTime + " [" + Thread.currentThread() + "] All thread is ready, concurrent going...");
    59. // 因开启门只需一个开关,所以立马就开启开始门
    60. startGate.countDown();
    61. // 等等结束门开启
    62. endGate.await();
    63. long endTime = System.nanoTime();
    64. System.out.println(endTime + " [" + Thread.currentThread() + "] All thread is completed.");
    65. return endTime - startTime;
    66. }

    }

如上,就可以发起真正的并发请求了。
并发请求操作流程示意图如下:
在这里插入图片描述
此处设置了一道门,以保证所有线程可以同时生效。但是,此处的同时启动,也只是语言层面的东西,也并非绝对的同时并发。具体的调用还要依赖于CPU个数,线程数及操作系统的线程调度功能等,不过咱们也无需纠结于这些了,重点在于理解原理!

CyclicBarrier

与 CountDownLatch 有类似功能的,还有个工具栅栏 CyclicBarrier, 也是提供一个等待所有线程到达某一点后,再一起开始某个动作,效果一致,不过栅栏的目的确实比较纯粹,就是等待所有线程到达,而前面说的闭锁 CountDownLatch 虽然实现的也是所有线程到达后再开始,但是他的触发点其实是 最后那一个开关,所以侧重点是不一样的。 简单看一下栅栏是如何实现真正同时并发呢?示例如下:

  1. // 与 闭锁 结构一致
  2. public class LatchTest {
  3. public static void main(String[] args) throws InterruptedException {
  4. Runnable taskTemp = new Runnable() {
  5. private int iCounter;
  6. @Override
  7. public void run() {
  8. // 发起请求
  9. // HttpClientOp.doGet("https://www.baidu.com/");
  10. iCounter++;
  11. System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter);
  12. }
  13. };
  14. LatchTest latchTest = new LatchTest();
  15. // latchTest.startTaskAllInOnce(5, taskTemp);
  16. latchTest.startNThreadsByBarrier(5, taskTemp);
  17. }
  18. public void startNThreadsByBarrier(int threadNums, Runnable finishTask) throws InterruptedException {
  19. // 设置栅栏解除时的动作,比如初始化某些值
  20. CyclicBarrier barrier = new CyclicBarrier(threadNums, finishTask);
  21. // 启动 n 个线程,与栅栏阀值一致,即当线程准备数达到要求时,栅栏刚好开启,从而达到统一控制效果
  22. for (int i = 0; i < threadNums; i++) {
  23. Thread.sleep(100);
  24. new Thread(new CounterTask(barrier)).start();
  25. }
  26. System.out.println(Thread.currentThread().getName() + " out over...");
  27. }
  28. }
  29. class CounterTask implements Runnable {
  30. // 传入栅栏,一般考虑更优雅方式
  31. private CyclicBarrier barrier;
  32. public CounterTask(final CyclicBarrier barrier) {
  33. this.barrier = barrier;
  34. }
  35. public void run() {
  36. System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " is ready...");
  37. try {
  38. // 设置栅栏,使在此等待,到达位置的线程达到要求即可开启大门
  39. barrier.await();
  40. } catch (InterruptedException e) {
  41. e.printStackTrace();
  42. } catch (BrokenBarrierException e) {
  43. e.printStackTrace();
  44. }
  45. System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " started...");
  46. }
  47. }

其运行结果如下图:
在这里插入图片描述

发表评论

表情:
评论列表 (有 0 条评论,97人围观)

还没有评论,来说两句吧...

相关阅读