Jmeter--自己写并发压测脚本一定会遇到的类
原文链接:https://mp.weixin.qq.com/s?__biz=MzUxMTgyNTQ2MA==&mid=2247484012&idx=1&sn=3a536e9cea99532eaca831c39c9e6620&chksm=f96c8137ce1b0821bdde3b8923ea41578270033a1bbc3ab4c20dc08f88c2ac2f4c817050f243&token=659961685&lang=zh_CN#rd
性能测试做到后面,一些特殊的场景利用常用的现成工具满足不了需求,所以你需要学习java写一些特定协议的压测脚本,那你不得不研究多线程或线程池,而此时你也一定会遇到java并发编程中的几个类,今天重点讲解这3个类,CountDownLanch、CyclicBarrier、Semaphore,希望大家以后看到的时候知道是干嘛用的。
接下来,我就最近学习的成果,下面和大家举例子讲解一下,帮助理解。
1CountDownLanch
场景
工作中往往会遇到需要异步处理的任务,此时我们就会利用多线程的方式去处理,即启动子线程去执行任务,而此时主线程阻塞,等待所有的子线程完成任务后,再去做一些汇总统计工作。
CountDownLanch 是一个倒数计数器, 给一个初始值(>=0), 然后每一次调用countDown就会减1, 这很符合等待多个子线程结束的场景: 一个线程结束的时候, countDown一次, 直到所有的线程都countDown了 , 那么所有子线程就都结束了。
先看看CountDownLanch提供的方法。
await: 会阻塞等待计数器减少到0位置. 带参数的await是多了等待时间。
countDown: 将当前的计数减1。
getCount(): 返回当前的计数。
显而易见, 我们只需要在子线程执行之前, 赋予初始化countDownLanch, 并赋予线程数量为初始值。
每个线程执行完毕的时候, 就countDown一下。主线程只需要调用await方法, 可以等待所有子线程执行结束。
例子
package ht.test; import java.util.concurrent.CountDownLatch; import java.util.concurrent.CyclicBarrier; public class CountDownLatchTest { // 参数是2表示对象执行2次countDown方法才能释放锁 static CountDownLatch c = new CountDownLatch(2); public static void main(String[] args) throws InterruptedException { // 定义线程数 int subThreadNum = 5; // 取得一个倒计时器,从5开始 CountDownLatch countDownLatch = new CountDownLatch(subThreadNum); // 依次创建5个线程,并启动 for (int i = 0; i < subThreadNum; i++) { SubThread runnablethread = new SubThread(5000, countDownLatch); new Thread(runnablethread).start(); } // 主线程工作,此处可以添加一些额外的工作 // mainWork(); // 等待所有的子线程结束 countDownLatch.await(); System.out.println("all all all Main Thread work done!"); } static class SubThread implements Runnable { private CountDownLatch countDownLatch; private long workTime; public SubThread(long workTime, CountDownLatch countDownLatch) { this.workTime = workTime; this.countDownLatch = countDownLatch; } public void run() { // TODO Auto-generated method stub try { System.out.println("线程Id:" + Thread.currentThread().getId() + " Sub thread is starting!"); Thread.sleep(workTime); System.out.println("线程Id:" + Thread.currentThread().getId() + " Sub thread is stopping!"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } finally { // 线程结束时,将计时器减一 countDownLatch.countDown(); } } } }
2CyclicBarrier
场景
我们在做压测的时候,如要真正的并发,那么在创建线程成功后需要等待其他线程也创建好了,一起等着,同时发送请求,此时就用到了CyclicBarrier。
CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续运行。
例子
package ht.test; import java.util.concurrent.CyclicBarrier; public class CyclicBarrierTest { // 初始化线程等待的个数,当调用await()函数的次数和这里的数值一致时候,接下去执行。 static CyclicBarrier c = new CyclicBarrier(2); public static void main(String[] args) throws Exception { new Thread(new Runnable() { @Override public void run() { try { // 子线程 c.await(); } catch (Exception e) { } System.out.println(1); } }).start(); try { // 主线程,若此处注册掉,执行结果为2,并且子线程一直阻塞着 c.await(); } catch (Exception e) { } System.out.println(2); } }
3Semaphore
场景
有时候并发太大的时候,我们需要人工的控制,譬如一些数据库的连接数这样子的,资源毕竟有限的,不可能无限去创建连接,此时我们就需要利用Semaphore去控制。
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。可以控制系统的流量,拿到信号量的线程可以进入,否则就等待。通过acquire()和release()获取和释放访问许可。
例子
package ht.test; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Semaphore; public class TestSemaphore { public static void main(String[] args) { // 线程池 ExecutorService exec = Executors.newCachedThreadPool(); // 只能5个线程同时访问 final Semaphore semp = new Semaphore(5); // 模拟20个客户端访问 for (int index = 0; index < 20; index++) { final int NO = index; Runnable run = new Runnable() { @Override public void run() { try { // 获取许可 semp.acquire(); System.out.println("Accessing: " + NO); // 模拟实际业务逻辑 Thread.sleep((long) 10000); // 访问完后,释放 semp.release(); } catch (InterruptedException e) { } } }; exec.execute(run); } try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } // System.out.println(semp.getQueueLength()); // 退出线程池 exec.shutdown(); } }
不知道,你有没有理解,在此只是抛砖引玉一下,大家在实践中可以继续学习研究。