Java 如何模拟真正的并发请求?
有时需要测试一下某个功能的并发性能,又不要想借助于其他工具,索性就自己的开发语言,来一个并发请求就最方便了。
java中模拟并发请求,自然是很方便的,只要多开几个线程,发起请求就好了。但是,这种请求,一般会存在启动的先后顺序了,算不得真正的同时并发!
怎么样才能做到真正的同时并发呢?
是本文想说的点,java中提供了闭锁 CountDownLatch, 刚好就用来做这种事就最合适了。
只需要:
开启n个线程,加一个闭锁,开启所有线程;
待所有线程都准备好后,按下开启按钮,就可以真正的发起并发请求了。
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;}
}
关注Java核心技术,推送更多 Java 干货!
如上,就可以发起真正的并发请求了。
并发请求操作流程示意图如下:
此处设置了一道门,以保证所有线程可以同时生效。但是,此处的同时启动,也只是语言层面的东西,也并非绝对的同时并发。具体的调用还要依赖于CPU个数,线程数及操作系统的线程调度功能等,不过咱们也无需纠结于这些了,重点在于理解原理!
与 CountDownLatch 有类似功能的,还有个工具栅栏 CyclicBarrier, 也是提供一个等待所有线程到达某一点后,再一起开始某个动作,效果一致,不过栅栏的目的确实比较纯粹,就是等待所有线程到达,而前面说的闭锁 CountDownLatch 虽然实现的也是所有线程到达后再开始,但是他的触发点其实是 最后那一个开关,所以侧重点是不一样的。
简单看一下栅栏是如何实现真正同时并发呢?示例如下:最新 Java 面试题分享给你看下。
// 与 闭锁 结构一致
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...");}
}
其运行结果如下图:
各有其应用场景吧,关键在于需求。就本文示例的需求来说,个人更愿意用闭锁一点,因为更可控了。但是代码却是多了,所以看你喜欢吧。
往期推荐
Redis配合Lua实现高并发防止秒杀超卖,实战源码解决方案
刷Java面试题容易忽略的API
批处理框架 Spring Batch 原理讲解
7款可视化工具,提高开发效率必备
Mysql数据库查询超时,这样优化快速解决问题
天秀!搞java的技术人写了本小说:《JavaScript百炼成仙》
55 个细节帮你全方位的完成Java 性能优化的 (珍藏)
Java 实现视频弹幕功能
Java的ClassLoader加载是怎么保证安全的?
回复【干货】获取精选干货视频教程
回复【加群】加入疑难问题攻坚交流群
回复【mat】获取内存溢出问题分析详细文档教程
回复【赚钱】获取用java写一个能赚钱的微信机器人
回复【副业】获取程序员副业攻略一份
戳这儿
Java 如何模拟真正的并发请求?相关推荐
- java可视化模拟进程的并发执行
java可视化模拟进程的并发执行 模拟内容 附录程序清单 效果展示 模拟内容 利用JAVA语言,模拟进程的并发执行的过程.一是体现在单CPU环境下,对CPU的竞争:二是体现在无明确调度算法支撑下,进程 ...
- jMeter 模拟 web 高并发请求
导航 一.jmeter 简介与下载 二.接口压测设置 三.实战演示 一.jmeter 简介与下载 Apache JMeter是Apache组织开发的基于Java的压力测试工具. 最初被设计用于Web应 ...
- Java 中如何模拟真正的同时并发请求?
有时需要测试一下某个功能的并发性能,又不要想借助于其他工具,索性就自己的开发语言,来一个并发请求就最方便了. java中模拟并发请求,自然是很方便的,只要多开几个线程,发起请求就好了.但是,这种请求, ...
- java 模拟多线程并发_Java中模拟同时并发请求
有时需要测试一下某个功能的并发性能,又不要想借助于其他工具,索性就自己的开发语言,来一个并发请求就最方便了. java中模拟并发请求,自然是很方便的,只要多开几个线程,发起请求就好了.但是,这种请求, ...
- java端模拟http的get、post请求(转)
Servlet是SUN指定的Java服务器端编程规范,用以处理来自客户端的请求,处理并做出响应的一套基础API.Servlet是运行在Servlet容器中的Java小程序,容器运行在服务器端,服务器侦 ...
- Java HttpURLConnection模拟请求Rest接口解决中文乱码问题
Java HttpURLConnection模拟请求Rest接口解决中文乱码问题 参考文章: (1)Java HttpURLConnection模拟请求Rest接口解决中文乱码问题 (2)https: ...
- Java——使用多线程模拟真实高并发业务并保证安全性(一)
作者专注于Java.架构.Linux.小程序.爬虫.自动化等技术. 工作期间含泪整理出一些资料,微信搜索[javaUp],回复 [java][黑客][爬虫][小程序][面试]等关键字免费获取资料.技术 ...
- Java模拟HTTP/POST方式请求接口
Java模拟HTTP/POST方式请求接口: java模拟http/post方式请求接口方法主体: public String sendPost(JSONObject json, String url ...
- java redis计数器_Redis原子计数器incr,防止并发请求
一.前言 在一些对高并发请求有限制的系统或者功能里,比如说秒杀活动,或者一些网站返回的当前用户过多,请稍后尝试.这些都是通过对同一时刻请求数量进行了限制,一般用作对后台系统的保护,防止系统因为过大的流 ...
最新文章
- 自动驾驶车通过动作捕捉,学会阅读街上人们的肢体语言
- SQL查询 的一些原则
- redis:RDM连接阿里云redis服务器
- mybatis mapper.xml 文件共用_MyBatis 缓存原来是这么一回事儿!| 原力计划
- OpenGL ES之3D渲染旋转的贴图立方体
- hibernate的缓存技术使用
- hadoop容灾能力测试
- html基础元素案例笔记(1)
- 浅谈 C++ 中的 new/delete 和 new[]/delete[]
- tomcat勾连mysql_tomcat9.0启动脚本startup.bat的分析
- 多线程学习笔记4 互斥体
- 水很深的深度学习-Task05循环神经网络RNN
- Python 目录及文件操作(os.模块)
- 文献管理三剑客之Mendeley最新版使用小记2
- html打印成功回调,web前端打印实现
- 微信小程序富文本插件WxParse使用
- 《utils》yaml,yml格式化
- MySQL生成随机姓名
- oracle创建PDB数据库
- 自监督学习(四)Joint Unsupervised Learning of Deep Representations and Image Clusters
热门文章
- Acwing 4268. 性感素数
- 2020软件测试工程师面试题汇总(内含答案)-看完BATJ面试官对你竖起大拇指!
- C语言思维基础的导图
- DecimalFormat的用法介绍 --转载
- 首域金融BOSCTIME_关于首域金融BOSCTIME|首域金融资料
- 手把手利用学校的教育邮箱
- 一个无名前端的10年前端路
- 原神私服 grasscutter搭建及食用教程
- http-server : 无法加载文件 C:\Users\mes\AppData\Roaming\npm\http-server.ps1,因为在此系统上禁止运行脚本。有关详细信息, 请参阅 http
- 钉钉授权时报获取钉钉用户信息失败,失败原因:访问ip不在白名单之中的解决办法