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

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

只需要:

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

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

package com.test;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.CountDownLatch;public class LatchTest {public static void main(String[] args) throws InterruptedException {Runnable taskTemp = new Runnable() {// 注意,此处是非线程安全的,留坑private int iCounter;@Overridepublic void run() {for(int i = 0; i < 10; i++) {// 发起请求
//                    HttpClientOp.doGet("https://www.baidu.com/");iCounter++;System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter);try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}}}};LatchTest latchTest = new LatchTest();latchTest.startTaskAllInOnce(5, taskTemp);}public long startTaskAllInOnce(int threadNums, final Runnable task) throws InterruptedException {final CountDownLatch startGate = new CountDownLatch(1);final CountDownLatch endGate = new CountDownLatch(threadNums);for(int i = 0; i < threadNums; i++) {Thread t = new Thread() {public void run() {try {// 使线程在此等待,当开始门打开时,一起涌入门中startGate.await();try {task.run();} finally {// 将结束门减1,减到0时,就可以开启结束门了endGate.countDown();}} catch (InterruptedException ie) {ie.printStackTrace();}}};t.start();}long startTime = System.nanoTime();System.out.println(startTime + " [" + Thread.currentThread() + "] All thread is ready, concurrent going...");// 因开启门只需一个开关,所以立马就开启开始门startGate.countDown();// 等等结束门开启endGate.await();long endTime = System.nanoTime();System.out.println(endTime + " [" + Thread.currentThread() + "] All thread is completed.");return endTime - startTime;}
}

其执行效果如下图所示:

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

class HttpClientOp {public static String doGet(String httpurl) {HttpURLConnection connection = null;InputStream is = null;BufferedReader br = null;String result = null;// 返回结果字符串try {// 创建远程url连接对象URL url = new URL(httpurl);// 通过远程url连接对象打开一个连接,强转成httpURLConnection类connection = (HttpURLConnection) url.openConnection();// 设置连接方式:getconnection.setRequestMethod("GET");// 设置连接主机服务器的超时时间:15000毫秒connection.setConnectTimeout(15000);// 设置读取远程返回的数据时间:60000毫秒connection.setReadTimeout(60000);// 发送请求connection.connect();// 通过connection连接,获取输入流if (connection.getResponseCode() == 200) {is = connection.getInputStream();// 封装输入流is,并指定字符集br = new BufferedReader(new InputStreamReader(is, "UTF-8"));// 存放数据StringBuffer sbf = new StringBuffer();String temp = null;while ((temp = br.readLine()) != null) {sbf.append(temp);sbf.append("\r\n");}result = sbf.toString();}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源if (null != br) {try {br.close();} catch (IOException e) {e.printStackTrace();}}if (null != is) {try {is.close();} catch (IOException e) {e.printStackTrace();}}connection.disconnect();// 关闭远程连接}return result;}public static String doPost(String httpUrl, String param) {HttpURLConnection connection = null;InputStream is = null;OutputStream os = null;BufferedReader br = null;String result = null;try {URL url = new URL(httpUrl);// 通过远程url连接对象打开连接connection = (HttpURLConnection) url.openConnection();// 设置连接请求方式connection.setRequestMethod("POST");// 设置连接主机服务器超时时间:15000毫秒connection.setConnectTimeout(15000);// 设置读取主机服务器返回数据超时时间:60000毫秒connection.setReadTimeout(60000);// 默认值为:false,当向远程服务器传送数据/写数据时,需要设置为trueconnection.setDoOutput(true);// 默认值为:true,当前向远程服务读取数据时,设置为true,该参数可有可无connection.setDoInput(true);// 设置传入参数的格式:请求参数应该是 name1=value1&name2=value2 的形式。connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");// 设置鉴权信息:Authorization: Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0connection.setRequestProperty("Authorization", "Bearer da3efcbf-0845-4fe3-8aba-ee040be542c0");// 通过连接对象获取一个输出流os = connection.getOutputStream();// 通过输出流对象将参数写出去/传输出去,它是通过字节数组写出的os.write(param.getBytes());// 通过连接对象获取一个输入流,向远程读取if (connection.getResponseCode() == 200) {is = connection.getInputStream();// 对输入流对象进行包装:charset根据工作项目组的要求来设置br = new BufferedReader(new InputStreamReader(is, "UTF-8"));StringBuffer sbf = new StringBuffer();String temp = null;// 循环遍历一行一行读取数据while ((temp = br.readLine()) != null) {sbf.append(temp);sbf.append("\r\n");}result = sbf.toString();}} catch (MalformedURLException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {// 关闭资源if (null != br) {try {br.close();} catch (IOException e) {e.printStackTrace();}}if (null != os) {try {os.close();} catch (IOException e) {e.printStackTrace();}}if (null != is) {try {is.close();} catch (IOException e) {e.printStackTrace();}}// 断开与远程地址url的连接connection.disconnect();}return result;}
}

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

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

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

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

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

// 与 闭锁 结构一致
public class LatchTest {public static void main(String[] args) throws InterruptedException {Runnable taskTemp = new Runnable() {private int iCounter;@Overridepublic void run() {// 发起请求
//              HttpClientOp.doGet("https://www.baidu.com/");iCounter++;System.out.println(System.nanoTime() + " [" + Thread.currentThread().getName() + "] iCounter = " + iCounter);}};LatchTest latchTest = new LatchTest();
//        latchTest.startTaskAllInOnce(5, taskTemp);latchTest.startNThreadsByBarrier(5, taskTemp);}public void startNThreadsByBarrier(int threadNums, Runnable finishTask) throws InterruptedException {// 设置栅栏解除时的动作,比如初始化某些值CyclicBarrier barrier = new CyclicBarrier(threadNums, finishTask);// 启动 n 个线程,与栅栏阀值一致,即当线程准备数达到要求时,栅栏刚好开启,从而达到统一控制效果for (int i = 0; i < threadNums; i++) {Thread.sleep(100);new Thread(new CounterTask(barrier)).start();}System.out.println(Thread.currentThread().getName() + " out over...");}
}class CounterTask implements Runnable {// 传入栅栏,一般考虑更优雅方式private CyclicBarrier barrier;public CounterTask(final CyclicBarrier barrier) {this.barrier = barrier;}public void run() {System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " is ready...");try {// 设置栅栏,使在此等待,到达位置的线程达到要求即可开启大门barrier.await();} catch (InterruptedException e) {e.printStackTrace();} catch (BrokenBarrierException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " - " + System.currentTimeMillis() + " started...");}
}

其运行结果如下图:

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

Java 中如何模拟真正的同时并发请求?相关推荐

  1. mockito java_使用Mockito在Java中进行模拟入门

    mockito java 我们都编写了单元测试,但是有时我们面临的挑战是被测单元可能依赖于其他组件. 并且配置其他组件进行单元测试绝对是一个过大的选择. 相反,我们可以使用Mocks代替其他组件,并继 ...

  2. 使用Mockito在Java中进行模拟入门

    我们都编写了单元测试,但是有时我们面临的挑战是被测单元可能依赖于其他组件. 并且配置其他组件进行单元测试绝对是一个过大的选择. 相反,我们可以使用Mocks代替其他组件,并继续进行单元测试. 为了说明 ...

  3. JAVA中J.U.C 包下并发类的应用

    文章目录 JUC包中的锁应用 Lock接口及ReentrantLock对象分析及应用? Condition接口对象分析与应用? ReadWriteLock接口及实现类分析与应用? StampedLoc ...

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

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

  5. 在Java中,使用HttpUtils实现发送HTTP请求

    HTTP请求,在日常开发中,还是比较常见的,今天给大家分享HttpUtils如何使用. 阅读本文,你将收获: 简单总结HTTP请求常用配置: JavaLib中HttpUtils如何使用: 如何封装HT ...

  6. Scrapy框架的学习(12. scrapy中的模拟登陆,发送post请求模拟登陆)

    1.在有的网站的的登录的界面上,有用户名以及输入密码的输入框 然后输入账号和密码进去,点击登录,后台就会把得到的数据发送过去,验证之后然后就登录成功了,就会跳转到 响应的登录之后的网站 2. 我们可以 ...

  7. java请求参数_在Java中发送http的post请求,设置请求参数等等

    前几天做了一个定时导入数据的接口,需要发送http请求,第一次做这种的需求,特地记一下子, 导包 import java.text.SimpleDateFormat; import java.util ...

  8. java 模拟多线程并发_Java中模拟同时并发请求

    有时需要测试一下某个功能的并发性能,又不要想借助于其他工具,索性就自己的开发语言,来一个并发请求就最方便了. java中模拟并发请求,自然是很方便的,只要多开几个线程,发起请求就好了.但是,这种请求, ...

  9. 【转载】谈谈我对Java中CallBack的理解

    谈谈我对Java中CallBack的理解 转载自: http://www.cnblogs.com/codingmyworld/archive/2011/07/22/2113514.html CallB ...

最新文章

  1. 独家 | 融资2.1亿商汤领投,他要用VR+AR解决无人驾驶的路测难题
  2. dell存储Linux配置,Dell MD3200远程存储的特殊配置
  3. python电脑版微信-python如何实现远程控制电脑(结合微信)
  4. 使用iframe call server及iframe target使用例
  5. 夏天雷雨天机房断电了,该怎么办?,叫你几招紧急应对方法
  6. 统计学习:基本常用公式(1)
  7. 小程序服务器七牛云,基于七牛云 API 开发的微信小程序 SDK
  8. mysql 存储过程 循环结构 命名_mysql存储过程----循环结构
  9. Win11关机后自动开机怎么办 Win11关机后自动开机的解决方法
  10. UTP网线和FTP网线,哪一种更适合超六类布线?
  11. 安卓wifi连接 UID xxx does not have permission to update configuration “xxx“WPA_PSK
  12. android系统 备份恢复,Android系统备份及系统还原方法介绍
  13. android bp文件_Android 基础 | Android.bp 语法浅析
  14. 模型评价 - 分类模型的常用评价指标
  15. 您的连接不是私密连接 thisisunsafe
  16. 全球与中国市场聚乙烯醇缩丁醛(PVB)树脂发展规模分析与前景战略研究报告2022年版
  17. CrackMe 之 006
  18. 洞态IAST自动检测S2-001漏洞
  19. Java第一学期 北大青鸟 第三章 我行我素购物管理系统:模拟幸运抽奖
  20. 相控阵天线面阵方向图(三种表示方法)

热门文章

  1. ProxySQL 监控和统计
  2. 云计算成IT反“腐”后盾-《中国电子报》2013年5月特刊
  3. Query String Object 2.1.7
  4. Jar文件的小小总结(原创)
  5. matlab的input函数总结
  6. OpenCV图像处理使用笔记(一)——使用鼠标选取任何形状的ROI
  7. Android Annotations配置与使用
  8. python自动测试u_自动化测试——Selenium+Python之下拉菜单的定位
  9. 全国铁路查询系统_火车票查询时刻表 全国列车时刻表 火车时刻表查询系统使用说明...
  10. tcp http https