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

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;}
}

关注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 如何模拟真正的并发请求?相关推荐

  1. java可视化模拟进程的并发执行

    java可视化模拟进程的并发执行 模拟内容 附录程序清单 效果展示 模拟内容 利用JAVA语言,模拟进程的并发执行的过程.一是体现在单CPU环境下,对CPU的竞争:二是体现在无明确调度算法支撑下,进程 ...

  2. jMeter 模拟 web 高并发请求

    导航 一.jmeter 简介与下载 二.接口压测设置 三.实战演示 一.jmeter 简介与下载 Apache JMeter是Apache组织开发的基于Java的压力测试工具. 最初被设计用于Web应 ...

  3. Java 中如何模拟真正的同时并发请求?

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

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

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

  5. java端模拟http的get、post请求(转)

    Servlet是SUN指定的Java服务器端编程规范,用以处理来自客户端的请求,处理并做出响应的一套基础API.Servlet是运行在Servlet容器中的Java小程序,容器运行在服务器端,服务器侦 ...

  6. Java HttpURLConnection模拟请求Rest接口解决中文乱码问题

    Java HttpURLConnection模拟请求Rest接口解决中文乱码问题 参考文章: (1)Java HttpURLConnection模拟请求Rest接口解决中文乱码问题 (2)https: ...

  7. Java——使用多线程模拟真实高并发业务并保证安全性(一)

    作者专注于Java.架构.Linux.小程序.爬虫.自动化等技术. 工作期间含泪整理出一些资料,微信搜索[javaUp],回复 [java][黑客][爬虫][小程序][面试]等关键字免费获取资料.技术 ...

  8. Java模拟HTTP/POST方式请求接口

    Java模拟HTTP/POST方式请求接口: java模拟http/post方式请求接口方法主体: public String sendPost(JSONObject json, String url ...

  9. java redis计数器_Redis原子计数器incr,防止并发请求

    一.前言 在一些对高并发请求有限制的系统或者功能里,比如说秒杀活动,或者一些网站返回的当前用户过多,请稍后尝试.这些都是通过对同一时刻请求数量进行了限制,一般用作对后台系统的保护,防止系统因为过大的流 ...

最新文章

  1. 自动驾驶车通过动作捕捉,学会阅读街上人们的肢体语言
  2. SQL查询 的一些原则
  3. redis:RDM连接阿里云redis服务器
  4. mybatis mapper.xml 文件共用_MyBatis 缓存原来是这么一回事儿!| 原力计划
  5. OpenGL ES之3D渲染旋转的贴图立方体
  6. hibernate的缓存技术使用
  7. hadoop容灾能力测试
  8. html基础元素案例笔记(1)
  9. 浅谈 C++ 中的 new/delete 和 new[]/delete[]
  10. tomcat勾连mysql_tomcat9.0启动脚本startup.bat的分析
  11. 多线程学习笔记4 互斥体
  12. 水很深的深度学习-Task05循环神经网络RNN
  13. Python 目录及文件操作(os.模块)
  14. 文献管理三剑客之Mendeley最新版使用小记2
  15. html打印成功回调,web前端打印实现
  16. 微信小程序富文本插件WxParse使用
  17. 《utils》yaml,yml格式化
  18. MySQL生成随机姓名
  19. oracle创建PDB数据库
  20. 自监督学习(四)Joint Unsupervised Learning of Deep Representations and Image Clusters

热门文章

  1. Acwing 4268. 性感素数
  2. 2020软件测试工程师面试题汇总(内含答案)-看完BATJ面试官对你竖起大拇指!
  3. C语言思维基础的导图
  4. DecimalFormat的用法介绍 --转载
  5. 首域金融BOSCTIME_关于首域金融BOSCTIME|首域金融资料
  6. 手把手利用学校的教育邮箱
  7. 一个无名前端的10年前端路
  8. 原神私服 grasscutter搭建及食用教程
  9. http-server : 无法加载文件 C:\Users\mes\AppData\Roaming\npm\http-server.ps1,因为在此系统上禁止运行脚本。有关详细信息, 请参阅 http
  10. 钉钉授权时报获取钉钉用户信息失败,失败原因:访问ip不在白名单之中的解决办法