使用微基准测试框架JMH对ReentrantLock的公平与非公平锁进行性能测试
前言
最近在阅读ReentrantLock源码的时候, 发现了Lock有公平和不公平两种锁, 查阅相关资料与博客后得知公平锁和非公平锁的性能是不一样的, 非公平锁的性能会优于公平锁.
因为FairLock在获取锁时, 永远是等待时间最长的线程获取到锁, 这样当线程释放锁以后, 如果还想继续再获取锁, 就必须去Sync队列尾部进行排队, 这样就会发生频繁的线程切换, 所以非公平锁的吞吐量就要强于公平锁
Benchmark
是openjdk提供一种基准测试的框架, 支持多种粒度的性能测试. 官方文档: https://github.com/openjdk/jmh
maven依赖
<!-- JMH-->
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-core</artifactId>
<version>${jmh.version}</version>
</dependency>
<dependency>
<groupId>org.openjdk.jmh</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>${jmh.version}</version>
<scope>provided</scope>
</dependency>
性能测试代码
利用CountDownLatch配合JMH, 测试极端条件下(获取锁之后立马释放锁, 不断循环), 不同的线程数量循环10000次加锁解锁的平均时间
代码地址
@Fork(1)
@Threads(1)
@Warmup(iterations = 1)
@State(Scope.Benchmark)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 5, timeUnit = TimeUnit.SECONDS)
public class BenchmarkFairAndNonFair {
@Param({"10", "50", "100", "300", "500"})
private int threadNum;
private static final int FOREACH_COUNT = 10000;
@Benchmark
public void testFairLock() throws InterruptedException {
final Lock fairLock = new ReentrantLock(true);
final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
threads.add(new Thread(() -> {
for (int j = 0; j < FOREACH_COUNT; j++) {
fairLock.lock();
fairLock.unlock();
}
countDownLatch.countDown();
}));
}
threads.forEach(thread -> thread.start());
// main wait all thread
countDownLatch.await();
}
@Benchmark
public void testNonFairLock() throws InterruptedException {
final Lock nonFairLock = new ReentrantLock(false);
final CountDownLatch countDownLatch = new CountDownLatch(threadNum);
List<Thread> threads = new ArrayList<>();
for (int i = 0; i < threadNum; i++) {
threads.add(new Thread(() -> {
for (int j = 0; j < FOREACH_COUNT; j++) {
nonFairLock.lock();
nonFairLock.unlock();
}
countDownLatch.countDown();
}));
}
threads.forEach(thread -> thread.start());
// main wait all thread
countDownLatch.await();
}
public static void main(String[] args) throws RunnerException {
Options options = new OptionsBuilder()
.include(BenchmarkFairAndNonFair.class.getSimpleName())
.build();
new Runner(options).run();
}
}
性能测试结果
# Run complete. Total time: 00:08:23
Benchmark (threadNum) Mode Cnt Score Error Units
BenchmarkFairAndNonFair.testFairLock 10 avgt 5 335.780 ± 13.599 ms/op
BenchmarkFairAndNonFair.testFairLock 50 avgt 5 1856.063 ± 127.977 ms/op
BenchmarkFairAndNonFair.testFairLock 100 avgt 5 3917.435 ± 673.222 ms/op
BenchmarkFairAndNonFair.testFairLock 300 avgt 5 14387.132 ± 3668.159 ms/op
BenchmarkFairAndNonFair.testFairLock 500 avgt 5 29424.262 ± 2485.551 ms/op
BenchmarkFairAndNonFair.testNonFairLock 10 avgt 5 2.383 ± 0.094 ms/op
BenchmarkFairAndNonFair.testNonFairLock 50 avgt 5 11.402 ± 0.820 ms/op
BenchmarkFairAndNonFair.testNonFairLock 100 avgt 5 23.525 ± 1.628 ms/op
BenchmarkFairAndNonFair.testNonFairLock 300 avgt 5 70.299 ± 0.746 ms/op
BenchmarkFairAndNonFair.testNonFairLock 500 avgt 5 121.046 ± 2.014 ms/op
结论
从测试结果可以可看出, 非公平锁的性能确实比公平锁要好, 而且线程数量越多, 差别越是明显