:::warning 参见: https://github.com/openjdk/jmh/blob/master/jmh-samples/src/main/java/org/openjdk/jmh/samples/JMHSample_15_Asymmetric.java :::
之前介绍的JMH全都是单线程的例子
但真正在生产中, 各种多线程并发的业务数不胜数
同样的代码, 在单线程中的执行效率,与多线程中截然不同, 不是简单的加一个线程速度就翻倍
如果多线程编程中竞态条件会极大的影响我们的执行效率
1. 例子
下面看一个模拟多线程竞争的代码
@State(Scope.Group)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.MICROSECONDS)
public class Sample_15_Asymmetric {
private AtomicInteger counter;
@Setup
public void up() {
counter = new AtomicInteger();
}
@Benchmark
@Group("benchmark_thread_competition")
@GroupThreads(3)
public int inc() {
return counter.incrementAndGet();
}
@Benchmark
@Group("benchmark_thread_competition")
@GroupThreads(1)
public int get() {
return counter.get();
}
}
2. 分析
在这个例子中
inc() 与 get() 是两个方法, 虽然标记了两个 @Benchmark, 但他们是同时执行的
因为我们标记了 @Group(“benchmark_thread_competition”) , 他们在同一个组中
然后
inc() 同时会有3个线程执行
get()同时会有1个线程执行
整个过程中会启动4个线程执行
3. 报表
看看执行结果
Benchmark Mode Cnt Score Error Units
Sample_15_Asymmetric.benchmark_thread_competition thrpt 154.785 ops/us
Sample_15_Asymmetric.benchmark_thread_competition:get thrpt 127.480 ops/us
Sample_15_Asymmetric.benchmark_thread_competition:inc thrpt 27.305 ops/us
4. 对照组1
上面的例子是在竞争AtomicInteger对象的时候的执行效率
我们再来看看, 如果没有竞争的话, 执行效率如何
先看看单线程自增操作耗时
@Benchmark
@Group("benchmark_inc_no_competition")
@GroupThreads(1)
public int inc1() {
return counter.incrementAndGet();
}
单独一个线程, 执行inc()
Benchmark Mode Cnt Score Error Units
Sample_15_Asymmetric.benchmark_inc_no_competition thrpt 164.339 ops/us
发现没, 这个例子中只有一个线程, 但执行效率却远远超过上面三个线程同时inc的效率, 效率大概在6倍左右
多线程反而让执行效率降低了
5. 对照组2
再看看但线程get操作耗时
@Benchmark
@Group("benchmark_get_no_competition")
public int get1() {
// 这个benchmark只有一个线程在执行get操作
// 效率直接拉满
return counter.get();
}
Benchmark Mode Cnt Score Error Units
Sample_15_Asymmetric.benchmark_get_no_competition thrpt 615.008 ops/us
执行效率是原来的5倍左右,
6. 总结
JMH对多线程的支持很完善, 提供了许多工具, 让我们有能力模拟真实的业务场景, 我们要善加利用