有时需要测试一下某个功能的并发性能,又不要想借助于其他工具,索性就自己的开发语言,来一个并发请求就最方便了。

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

  只需要:

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

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

  1. package com.test;
  2.  
  3. import java.io.BufferedReader;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.io.InputStreamReader;
  7. import java.io.OutputStream;
  8. import java.net.HttpURLConnection;
  9. import java.net.MalformedURLException;
  10. import java.net.URL;
  11. import java.util.concurrent.CountDownLatch;
  12.  
  13. public class LatchTest {
  14.  
  15. public static void main(String[] args) throws InterruptedException {
  16. Runnable taskTemp = new Runnable() {
  17.  
  18.        // 注意,此处是非线程安全的,留坑
  19. private int iCounter;
  20.  
  21. @Override
  22. public void run() {
  23. for(int i = 0; i < 10; i++) {
  24. // 发起请求
  25. // HttpClientOp.doGet("https://www.baidu.com/");
  26. iCounter++;
  27. System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter);
  28. try {
  29. Thread.sleep(100);
  30. } catch (InterruptedException e) {
  31. e.printStackTrace();
  32. }
  33. }
  34. }
  35. };
  36.  
  37. LatchTest latchTest = new LatchTest();
  38. latchTest.startTaskAllInOnce(5, taskTemp);
  39. }
  40.  
  41. public long startTaskAllInOnce(int threadNums, final Runnable task) throws InterruptedException {
  42. final CountDownLatch startGate = new CountDownLatch(1);
  43. final CountDownLatch endGate = new CountDownLatch(threadNums);
  44. for(int i = 0; i < threadNums; i++) {
  45. Thread t = new Thread() {
  46. public void run() {
  47. try {
  48. // 使线程在此等待,当开始门打开时,一起涌入门中
  49. startGate.await();
  50. try {
  51. task.run();
  52. } finally {
  53. // 将结束门减1,减到0时,就可以开启结束门了
  54. endGate.countDown();
  55. }
  56. } catch (InterruptedException ie) {
  57. ie.printStackTrace();
  58. }
  59. }
  60. };
  61. t.start();
  62. }
  63. long startTime = System.nanoTime();
  64. System.out.println(startTime + " [" + Thread.currentThread() + "] All thread is ready, concurrent going...");
  65. // 因开启门只需一个开关,所以立马就开启开始门
  66. startGate.countDown();
  67. // 等等结束门开启
  68. endGate.await();
  69. long endTime = System.nanoTime();
  70. System.out.println(endTime + " [" + Thread.currentThread() + "] All thread is completed.");
  71. return endTime - startTime;
  72. }
  73. }

其执行效果如下图所示:

httpClientOp 工具类,可以使用 成熟的工具包,也可以自己写一个简要的访问方法,参考如下:

  1. class HttpClientOp {
  2. public static String doGet(String httpurl) {
  3. HttpURLConnection connection = null;
  4. InputStream is = null;
  5. BufferedReader br = null;
  6. String result = null;// 返回结果字符串
  7. try {
  8. // 创建远程url连接对象
  9. URL url = new URL(httpurl);
  10. // 通过远程url连接对象打开一个连接,强转成httpURLConnection类
  11. connection = (HttpURLConnection) url.openConnection();
  12. // 设置连接方式:get
  13. connection.setRequestMethod("GET");
  14. // 设置连接主机服务器的超时时间:15000毫秒
  15. connection.setConnectTimeout(15000);
  16. // 设置读取远程返回的数据时间:60000毫秒
  17. connection.setReadTimeout(60000);
  18. // 发送请求
  19. connection.connect();
  20. // 通过connection连接,获取输入流
  21. if (connection.getResponseCode() == 200) {
  22. is = connection.getInputStream();
  23. // 封装输入流is,并指定字符集
  24. br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  25. // 存放数据
  26. StringBuffer sbf = new StringBuffer();
  27. String temp = null;
  28. while ((temp = br.readLine()) != null) {
  29. sbf.append(temp);
  30. sbf.append("\r\n");
  31. }
  32. result = sbf.toString();
  33. }
  34. } catch (MalformedURLException e) {
  35. e.printStackTrace();
  36. } catch (IOException e) {
  37. e.printStackTrace();
  38. } finally {
  39. // 关闭资源
  40. if (null != br) {
  41. try {
  42. br.close();
  43. } catch (IOException e) {
  44. e.printStackTrace();
  45. }
  46. }
  47.  
  48. if (null != is) {
  49. try {
  50. is.close();
  51. } catch (IOException e) {
  52. e.printStackTrace();
  53. }
  54. }
  55.  
  56. connection.disconnect();// 关闭远程连接
  57. }
  58.  
  59. return result;
  60. }
  61.  
  62. public static String doPost(String httpUrl, String param) {
  63.  
  64. HttpURLConnection connection = null;
  65. InputStream is = null;
  66. OutputStream os = null;
  67. BufferedReader br = null;
  68. String result = null;
  69. try {
  70. URL url = new URL(httpUrl);
  71. // 通过远程url连接对象打开连接
  72. connection = (HttpURLConnection) url.openConnection();
  73. // 设置连接请求方式
  74. connection.setRequestMethod("POST");
  75. // 设置连接主机服务器超时时间:15000毫秒
  76. connection.setConnectTimeout(15000);
  77. // 设置读取主机服务器返回数据超时时间:60000毫秒
  78. connection.setReadTimeout(60000);
  79.  
  80. // 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为true
  81. connection.setDoOutput(true);
  82. // 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无
  83. connection.setDoInput(true);
  84. // 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。
  85. connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
  86. // 设置鉴权信息:Authorization: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0
  87. connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0");
  88. // 通过连接对象获取一个输出流
  89. os = connection.getOutputStream();
  90. // 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的
  91. os.write(param.getBytes());
  92. // 通过连接对象获取一个输入流,向远程读取
  93. if (connection.getResponseCode() == 200) {
  94.  
  95. is = connection.getInputStream();
  96. // 对输入流对象进行包装:charset根据工作项目组的要求来设置
  97. br = new BufferedReader(new InputStreamReader(is, "UTF-8"));
  98.  
  99. StringBuffer sbf = new StringBuffer();
  100. String temp = null;
  101. // 循环遍历一行一行读取数据
  102. while ((temp = br.readLine()) != null) {
  103. sbf.append(temp);
  104. sbf.append("\r\n");
  105. }
  106. result = sbf.toString();
  107. }
  108. } catch (MalformedURLException e) {
  109. e.printStackTrace();
  110. } catch (IOException e) {
  111. e.printStackTrace();
  112. } finally {
  113. // 关闭资源
  114. if (null != br) {
  115. try {
  116. br.close();
  117. } catch (IOException e) {
  118. e.printStackTrace();
  119. }
  120. }
  121. if (null != os) {
  122. try {
  123. os.close();
  124. } catch (IOException e) {
  125. e.printStackTrace();
  126. }
  127. }
  128. if (null != is) {
  129. try {
  130. is.close();
  131. } catch (IOException e) {
  132. e.printStackTrace();
  133. }
  134. }
  135. // 断开与远程地址url的连接
  136. connection.disconnect();
  137. }
  138. return result;
  139. }
  140. }

  如上,就可以发起真正的并发请求了。

并发请求操作流程示意图如下:

  此处设置了一道门,以保证所有线程可以同时生效。但是,此处的同时启动,也只是语言层面的东西,也并非绝对的同时并发。具体的调用还要依赖于CPU个数,线程数及操作系统的线程调度功能等,不过咱们也无需纠结于这些了,重点在于理解原理!

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

  简单看一下栅栏是如何实现真正同时并发呢?示例如下:

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

其运行结果如下图:

  各有其应用场景吧,关键在于需求。就本文示例的需求来说,个人更愿意用闭锁一点,因为更可控了。但是代码却是多了,所以看你喜欢吧!

java中如何模拟真正的同时并发请求?的更多相关文章

  1. oracle EBS中使用PLSQL提交"关闭离散"并发请求

    declare l_request_id number; l_return_flag boolean; l_num_user_id number; l_num_resp_id number; l_nu ...

  2. java中多线程模拟(多生产,多消费,Lock实现同步锁,替代synchronized同步代码块)

    import java.util.concurrent.locks.*; class DuckMsg{ int size;//烤鸭的大小 String id;//烤鸭的厂家和标号 DuckMsg(){ ...

  3. Java中发送http的get、post请求

    近期做项目中,须要把消息通过中间件的形式通过http请求的方式推送给第三方,因此用到了http协议,小编花费了一个多小时.对于http协议中的post和get请求,封装了一个工具类.以下与大家分享一下 ...

  4. 谈谈我对Java中CallBack的理解

    谈谈我对Java中CallBack的理解 http://www.cnblogs.com/codingmyworld/archive/2011/07/22/2113514.html CallBack是回 ...

  5. 在 java 中 wait 和 sleep 方法的不同?

    最大的不同是在等待时 wait 会释放锁,而 sleep 一直持有锁.Wait 通常被用于线 程间交互,sleep 通常被用于暂停执行. 直接了解的深入一点吧: 在 Java 中线程的状态一共被分成  ...

  6. Java中不同的并发实现的性能比较

    Fork/Join框架在不同配置下的表现如何? 正如即将上映的星球大战那样,Java 8的并行流也是毁誉参半.并行流(Parallel Stream)的语法糖就像预告片里的新型光剑一样令人兴奋不已.现 ...

  7. Java中使用多线程、curl及代理IP模拟post提交和get访问

    Java中使用多线程.curl及代理IP模拟post提交和get访问 菜鸟,多线程好玩就写着玩,大神可以路过指教,小弟在这受教,谢谢! 更多分享请关注微信公众号:lvxing1788 ~~~~~~ 分 ...

  8. java中并发Queue种类与各自API特点以及使用场景!

    一 先说下队列 队列是一种数据结构.它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素(注意不要弄混队列的头部和尾部) 就是说,队列以一种先进先出的方式管理数据,如果你试图向一个 已经 ...

  9. Java中的并发编程集合使用

    一.熟悉Java自带的并发编程集合 在java.util.concurrent包里有很多并发编程的常用工具类. package com.ietree.basicskill.mutilthread.co ...

随机推荐

  1. eclipse java tomcat 远程调试

    在远程linux上修改tomcat 中bin 文件夹下 修改catalina.sh文件,在最前面加上如下代码: CATALINA_OPTS="-Xdebug -Xrunjdwp:transp ...

  2. 对DOM,SAX,JDOM,DOM4J四种方法解析XML文件的分析

    1.DOM 与平台无关的官方解析方式 DOM是一次性把xml文件加载到内存中,形成一个节点树 对内存有要求 2.SAX java提供的基于事件驱动的解析方式 每次遇到一个标签,会触发相应的事件方法 3 ...

  3. mysql 数据库的备份和还原

    1. 逻辑备份 (和存储引擎无关) mysqldump -uroot -p schoolDB TSubject > /mysqlbackup/schoolDB.TSubject.sql  (备份 ...

  4. Scikit-learn 安装

    Scikit-Learn 3 pip 安装 如果安装了Python,没有安装pip,使用Windows + R,输入cmd,回车打开命令行,输入 python -m pip install -U pi ...

  5. Transform(变换)—Y轴lable内容旋转

    <!DOCTYPE html> <html> <head> <style> div{ border:1px solid; } .bb{ position ...

  6. python微信自动回复

    模块是itchat 下载:命令行输入 pip install itchat import itchat #导入itchat模块 itchat.auto_login() #登陆微信,授权 用命令行发送给 ...

  7. fromdata上传多个文件

    function upload_single_file(value){ if(value==''){ layer.msg('请添加文件',{time:1500}) }else{ var formDat ...

  8. JavaScript基础视频教程总结(071-080章)

    <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...

  9. 学以致用三十一-----IPAddressField has been removed

    python 和 django版本 在进行makemigrations的时候报错 设置的字段 class Servers(models.Model): '''服务器信息''' hostname = m ...

  10. spring filter lister servlet

    https://blog.csdn.net/nacey5201/article/details/8547772 https://blog.csdn.net/xwl617756974/article/d ...