CountDownLatch和CyclicBarrier模拟同时并发请求
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
CountDownLatch和CyclicBarrier模拟同时并发请求
有时候要测试⼀下某个功能的并发能⼒,⼜不要想借助于其他测试⼯具,索性就⾃⼰写简单的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 .HttpURLConnection;
import .MalformedURLException;
import .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:///");
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:///");
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 还是很⽅便的.。