有时候要测试一下某个功能的并发能力,又不要想借助于其他测试工具,索性就自己写简单的demo模拟一个并发请求就最方便了。如果熟悉jemter的测试某接口的并发能力其实更专业,此处只是自己折腾着玩。

CountDownLatch和CyclicBarrier是jdk concurrent包下非常有用的两个并发工具类,它们提供了一种控制并发流程的手段。其实查看源码它们都是在内部维护了一个计数器控制流程的

  • CountDownLatch:一个或者多个线程,等待其他多个线程完成某件事情之后才能执行;
  • CyclicBarrier:多个线程互相等待,直到到达同一个同步点,再继续一起执行。   

CountDownLatch和CyclicBarrier的区别

  • CountDownLatch的计数器,线程完成一个记录一个,计数器是递减  计数器,只能使用一次
  • CyclicBarrier的计数器 更像是一个阀门,需要所有线程都到达,阀门才能打开,然后继续执行,计数器是递增  计数器提供reset功能,可以多次使用

   另外Semaphore可以控同时访问的线程个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

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

  下面分别使用CountDownLatch和CyclicBarrier来模拟并发的请求

CountDownLatch模拟

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

执行结果

CyclicBarrier模拟

// 与 闭锁 结构一致
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...");
}
}

执行结果

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

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

  毕竟测试并发 还得用专业的工具 jmeter 还是很方便的.

CountDownLatch和CyclicBarrier模拟同时并发请求的更多相关文章

  1. 使用Java辅助类(CountDownLatch、CyclicBarrier、Semaphore)并发编程

    在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch,CyclicBarrier和Semaphore,今天我们就来学习一下这三个辅助类的用法 一.C ...

  2. 【apache】apache模拟高并发请求

    目的:测试程序的性能 运用的工具是apache的ab工具,装有apache服务器的一般都有ab工具. lamp命令: ab -c 10 -n 100 "http://a.ilanni.com ...

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

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

  4. 并发编程JUC系列AQS(CountDownLatch、CyclicBarrier、Semaphore)

    一.CountDownLatch package com.jonychen.test; import java.util.concurrent.CountDownLatch; import java. ...

  5. Java并发编程: CountDownLatch、CyclicBarrier和 Semaphore

    java 1.5提供了一些非常有用的辅助类来帮助并发编程,比如CountDownLatch,CyclicBarrier和Semaphore. 1.CountDownLatch –主线程阻塞等待,最后完 ...

  6. java多线程10:并发工具类CountDownLatch、CyclicBarrier和Semaphore

    在JDK的并发包(java.util.concurrent下)中给开发者提供了几个非常有用的并发工具类,让用户不需要再去关心如何在并发场景下写出同时兼顾线程安全性与高效率的代码. 本文分别介绍Coun ...

  7. Java并发编程:CountDownLatch、CyclicBarrier和Semaphore

    Java并发编程:CountDownLatch.CyclicBarrier和Semaphore 在java 1.5中,提供了一些非常有用的辅助类来帮助我们进行并发编程,比如CountDownLatch ...

  8. Java并发(8):CountDownLatch、CyclicBarrier、Semaphore、Callable、Future

    CountDownLatch.CyclicBarrier.Semaphore.Callable.Future  都位于java.util.concurrent包下,其中CountDownLatch.C ...

  9. 使用CountDownLatch和CyclicBarrier处理并发线程

    闲话不说,首先看一段代码: { IValueCallback remoteCallback = new IValueCallback.Stub() { <strong><span s ...

随机推荐

  1. 章节七、2-Linked List

    package introduction9; import java.util.ArrayList; import java.util.LinkedList; import java.util.Lis ...

  2. 适用于VS C++环境的注释代码段,可以让你的代码被使用时有高可读性的注释

    编码时,在对高级语言(C#/VB etc)函数的访问时,经常会有很明确的函数功能提示,参数提示,与返回值提示.微软的VisualStudio C++集成开发环境同样有这样的功能,只是常见开源的代码很少 ...

  3. SQL Server OPTION (OPTIMIZE FOR UNKNOWN) 测试总结

      关于SQL Server的查询提示OPTION (OPTIMIZE FOR UNKNOWN) ,它是解决参数嗅探的方法之一. 而且对应的SQL语句会缓存,不用每次都重编译.关键在于它的执行计划的准 ...

  4. win7 中 sql server2005 卸载简介

    注:卸载前一定要做好备份,一定要清理干净,不然重装会出错(只针对完全卸载,没试过只删除一个版本的) 工具:①Windows Install Clean Up  ②SrvInstw.exe 1.停止所有 ...

  5. layui 的 GitHub 及 Gitee (码云) 仓库

    GitHub: https://github.com/sentsin/layui/ Gitee:https://gitee.com/sentsin/layui

  6. 自定义实现HashMap的put、get方法

    public class HashMap{ public static void main(String[] args){ put("aa", "wo ai ni&quo ...

  7. C语言 求两数的最大公约数和最小公倍数

    //作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ #include<stdio.h> //最大公约数 int gys(int x,int ...

  8. 一个tomcat部署多个应用实例

    安装JDK7sudo apt-get install java7-jdk 安装tomcat7 Tomcat7下载地址http://mirror.bjtu.edu.cn/apache/tomcat/to ...

  9. 17.基于scrapy-redis两种形式的分布式爬虫

    redis分布式部署 1.scrapy框架是否可以自己实现分布式? - 不可以.原因有二. 其一:因为多台机器上部署的scrapy会各自拥有各自的调度器,这样就使得多台机器无法分配start_urls ...

  10. ubuntu 环境下编译 hadoop 2.6.0的简单方法

    由于服务器一般都64位系统, hadoop网站的release版本32位native库不能运行,所以需要自己在编译一下.以下是我采用的一个编译的过程,比较简单,不用下载各种版本及环境配置,通过命令就能 ...