测试模型
基于JMH基准测试库
测试代码
package com.lsy.study.benchmark;
import org.openjdk.jmh.annotations.*;
import org.openjdk.jmh.results.format.ResultFormatType;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.StampedLock;
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@State(Scope.Thread)
@Warmup(iterations = 3, time = 1, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 10, time = 2, timeUnit = TimeUnit.SECONDS)
@Fork(1)
@Threads(2)
public class SyncTest {
static class TaskStack {
private volatile int task;
TaskStack() {
this.task = 10000000;
}
int getTask() {
this.task = this.task - 1;
return this.task;
}
}
@Setup
public void prepare() {
}
@Benchmark
public void synchronizedKeyWord() {
Object lock = new Object();
CountDownLatch countDownLatch = new CountDownLatch(50);
TaskStack taskStack = new TaskStack();
for (int i = 0; i < 50; i++) {
new Thread(() -> {
while (true) {
synchronized (lock) {
int task = taskStack.getTask();
if (task <= 0) {
break;
}
}
}
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Benchmark
public void reenterLock() {
ReentrantLock reentrantLock = new ReentrantLock();
CountDownLatch countDownLatch = new CountDownLatch(50);
TaskStack taskStack = new TaskStack();
for (int i = 0; i < 50; i++) {
new Thread(() -> {
while (true) {
reentrantLock.lock();
try {
int task = taskStack.getTask();
if (task <= 0) {
break;
}
} finally {
reentrantLock.unlock();
}
}
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Benchmark
public void stampedLock() {
StampedLock stampedLock = new StampedLock();
CountDownLatch countDownLatch = new CountDownLatch(50);
TaskStack taskStack = new TaskStack();
for (int i = 0; i < 50; i++) {
new Thread(() -> {
while (true) {
stampedLock.writeLock();
try {
int task = taskStack.getTask();
if (task <= 0) {
break;
}
} finally {
stampedLock.tryUnlockWrite();
}
}
countDownLatch.countDown();
}).start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Options opts = new OptionsBuilder()
.include(SyncTest.class.getSimpleName())
.resultFormat(ResultFormatType.JSON)
.build();
new Runner(opts).run();
}
}
结论图
样本一
参数:
20个线程
1000w个枪锁任务
结果
ReentrantLock: 平均用时263毫秒
StampedLock: 平均用时263毫秒
synchronized: 平均用时454毫秒
样本二
参数:
50个线程
1000w个枪锁任务
结果
ReentrantLock: 平均用时271毫秒
StampedLock: 平均用时297毫秒
synchronized: 平均用时465毫秒
样本三
参数:
50个线程
1w个枪锁任务
结果
ReentrantLock: 平均用时8.0毫秒
StampedLock: 平均用时8.1毫秒
synchronized: 平均用时8.9毫秒
样本四
其实答案已经明显,用ReenterLock是为了高级特性,真要是性能区别也没多少,谁会真有几千万的并发任务在本地跑,真有synchronized关键字也简单明了,代码整洁;