java中如何模拟真正的同时并发请求?
有时需要测试一下某个功能的并发性能,又不要想借助于其他工具,索性就自己的开发语言,来一个并发请求就最方便了。
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;
- @Override
- public 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();
- // 设置连接方式:get
- connection.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,当向远程服务器传送数据/写数据时,需要设置为true
- connection.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-ee040be542c0
- connection.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;
- @Override
- public 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中如何模拟真正的同时并发请求?的更多相关文章
- oracle EBS中使用PLSQL提交"关闭离散"并发请求
declare l_request_id number; l_return_flag boolean; l_num_user_id number; l_num_resp_id number; l_nu ...
- java中多线程模拟(多生产,多消费,Lock实现同步锁,替代synchronized同步代码块)
import java.util.concurrent.locks.*; class DuckMsg{ int size;//烤鸭的大小 String id;//烤鸭的厂家和标号 DuckMsg(){ ...
- Java中发送http的get、post请求
近期做项目中,须要把消息通过中间件的形式通过http请求的方式推送给第三方,因此用到了http协议,小编花费了一个多小时.对于http协议中的post和get请求,封装了一个工具类.以下与大家分享一下 ...
- 谈谈我对Java中CallBack的理解
谈谈我对Java中CallBack的理解 http://www.cnblogs.com/codingmyworld/archive/2011/07/22/2113514.html CallBack是回 ...
- 在 java 中 wait 和 sleep 方法的不同?
最大的不同是在等待时 wait 会释放锁,而 sleep 一直持有锁.Wait 通常被用于线 程间交互,sleep 通常被用于暂停执行. 直接了解的深入一点吧: 在 Java 中线程的状态一共被分成 ...
- Java中不同的并发实现的性能比较
Fork/Join框架在不同配置下的表现如何? 正如即将上映的星球大战那样,Java 8的并行流也是毁誉参半.并行流(Parallel Stream)的语法糖就像预告片里的新型光剑一样令人兴奋不已.现 ...
- Java中使用多线程、curl及代理IP模拟post提交和get访问
Java中使用多线程.curl及代理IP模拟post提交和get访问 菜鸟,多线程好玩就写着玩,大神可以路过指教,小弟在这受教,谢谢! 更多分享请关注微信公众号:lvxing1788 ~~~~~~ 分 ...
- java中并发Queue种类与各自API特点以及使用场景!
一 先说下队列 队列是一种数据结构.它有两个基本操作:在队列尾部加入一个元素,和从队列头部移除一个元素(注意不要弄混队列的头部和尾部) 就是说,队列以一种先进先出的方式管理数据,如果你试图向一个 已经 ...
- Java中的并发编程集合使用
一.熟悉Java自带的并发编程集合 在java.util.concurrent包里有很多并发编程的常用工具类. package com.ietree.basicskill.mutilthread.co ...
随机推荐
- eclipse java tomcat 远程调试
在远程linux上修改tomcat 中bin 文件夹下 修改catalina.sh文件,在最前面加上如下代码: CATALINA_OPTS="-Xdebug -Xrunjdwp:transp ...
- 对DOM,SAX,JDOM,DOM4J四种方法解析XML文件的分析
1.DOM 与平台无关的官方解析方式 DOM是一次性把xml文件加载到内存中,形成一个节点树 对内存有要求 2.SAX java提供的基于事件驱动的解析方式 每次遇到一个标签,会触发相应的事件方法 3 ...
- mysql 数据库的备份和还原
1. 逻辑备份 (和存储引擎无关) mysqldump -uroot -p schoolDB TSubject > /mysqlbackup/schoolDB.TSubject.sql (备份 ...
- Scikit-learn 安装
Scikit-Learn 3 pip 安装 如果安装了Python,没有安装pip,使用Windows + R,输入cmd,回车打开命令行,输入 python -m pip install -U pi ...
- Transform(变换)—Y轴lable内容旋转
<!DOCTYPE html> <html> <head> <style> div{ border:1px solid; } .bb{ position ...
- python微信自动回复
模块是itchat 下载:命令行输入 pip install itchat import itchat #导入itchat模块 itchat.auto_login() #登陆微信,授权 用命令行发送给 ...
- fromdata上传多个文件
function upload_single_file(value){ if(value==''){ layer.msg('请添加文件',{time:1500}) }else{ var formDat ...
- JavaScript基础视频教程总结(071-080章)
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title&g ...
- 学以致用三十一-----IPAddressField has been removed
python 和 django版本 在进行makemigrations的时候报错 设置的字段 class Servers(models.Model): '''服务器信息''' hostname = m ...
- spring filter lister servlet
https://blog.csdn.net/nacey5201/article/details/8547772 https://blog.csdn.net/xwl617756974/article/d ...