三、并发模拟工具JMeter
JMeter也是一款性能测试工具,是图形化的。下载地址:传送门 http://jmeter.apache.org/
需要 Java8+ 的环境
解压到你觉得合适的目录下(注意最好是英文路径),进入它的 bin 目录下 启动 jmeter.bat 即可。
使用很简单,首先在测试计划部分新建一个线程组
设置好基础信息后添加HTTP请求(基本信息设置好没有OK哈,直接添加HTTP请求)
填写HTTP请求相关的内容
之后还要添加监听器,这里选择是图形结果
再添加一个查看结果树吧
在运行之前打开log Viewer
下面开始运行:
执行成功,来感受一下结果:
点进去
查看结果树
四、代码模拟
这里需要用到一个类,就是 CountDownLatch。CountDownLatch 是一个计数器闭锁,通过它可以完成类似于阻塞当前线程的功能,即:一个线程或多个线程一直等待,直到其他线程执行的操作完成。
更多多线程教程可以参考:https://www.javastack.cn/categories/Java/
CountDownLatch 用一个给定的计数器来初始化,该计数器的操作是原子操作,即同时只能有一个线程去操作该计数器。调用该类await方法的线程会一直处于阻塞状态,直到其他线程调用 countDown 方法使当前计数器的值变为零,每次调用 countDown 计数器的值减1。
当计数器值减至零时,所有因调用await()方法而处于等待状态的线程就会继续往下执行。这种现象只会出现一次,因为计数器不能被重置。下图和它的方法可以体现出来:
CountDownLatch类只提供了一个构造器:
public CountDownLatch(int count) { };
然后下面这 3 个方法是 CountDownLatch 类中最重要的方法(上图能够反映出来)
public void await() throws InterruptedException { }; public boolean await(long timeout, TimeUnit unit) throws InterruptedException { }; public void countDown() { };
下面还需要看一个类 Semaphore
Semaphore 与 CountDownLatch 相似,不同的地方在于 Semaphore 的值被获取到后是可以释放的,并不像 CountDownLatch 那样一直减到底。
它也被更多地用来限制流量,类似阀门的 功能。如果限定某些资源最多有N个线程可以访问,那么超过N个主不允许再有线程来访问,同时当现有线程结束后,就会释放,然后允许新的线程进来。有点类似于锁的lock与 unlock过程。相对来说他也有两个主要的方法:
用于获取权限的acquire(),其底层实现与CountDownLatch.countdown()类似;用于释放权限的release(),其底层实现与acquire()是一个互逆的过程。
通过这两个类可以进行并发的模拟:
测试一下:
import lombok.extern.slf4j.Slf4j; import java.util.concurrent.*; @Slf4j public class CuncurrencyTest { public static int clientTotal = 5000; public static int threadTotal = 200; public static int count = 0; public static void main(String[] args) throws InterruptedException { ExecutorService executorService = Executors.newCachedThreadPool(); final Semaphore semaphore = new Semaphore(threadTotal); final CountDownLatch countDownLatch = new CountDownLatch(clientTotal); for (int i = 0; i < clientTotal; i++) { executorService.execute(() -> { try { semaphore.acquire(); add(); semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); log.error("exception",e); } countDownLatch.countDown(); }); } countDownLatch.await(); executorService.shutdown(); log.info("count:{}",count); } private static void add() { count++; } }
因为 count 不是线程安全的,且没有作防护措施,结果是错的
上面是对代码的并发模拟的简单形式,值得注意的是,这里提到的两个类不是专门做并发模拟,它们的用途很广泛,等之后更新Java网络编程的东西的时候,还会详细介绍它们。