最近写了个例子,比较了一下synchronized和ReentrantLock的性能,分享一下数据和个人观点。
public class SycLockTest {
static String case1File = "c1.txt";
static String case2File = "c2.txt";
static volatile FileWriter caseWriter;
static BlockingQueue<Long> dataQ;
static String fileName="a.txt";
static CountDownLatch countdown;
static Object lock =new Object();
static Lock reentrantLock = new ReentrantLock();
static int x=10;
volatile static List<Long> record;
static int before = 500;
static int redo = 50;
static int vl=0;
static BlockingQueue bq=new ArrayBlockingQueue(1000);
static Object[] abc= new Object[100];
static void fgh(){
cal(before);
long start = System.nanoTime();
synchronized (lock){
cal(redo);
//writeFile();
//add();
}
long cost =System.nanoTime()-start;
addRecord(cost);
dataQ.add(cost);
//System.out.println(Thread.currentThread().getName());
countdown.countDown();
}
static void efg(){
cal(before);
long start = System.nanoTime();
reentrantLock.lock();
cal(redo);
//writeFile();
//add();
reentrantLock.unlock();
long cost =System.nanoTime()-start;
addRecord(cost);
dataQ.add(cost);
//System.out.println(Thread.currentThread().getName());
countdown.countDown();
}
static void add(){
// vl++;
// vl--;
// bq.add(lock);
// bq.remove(lock);
// bq.add(lock);
// bq.remove(lock);
for(int a=0;a<abc.length;a++){
abc[a]=lock;
}
}
static void cal(int repeat){
while (repeat-->0){
x= 1<<32;
}
}
static void writeFile() {
try(FileWriter fw = new FileWriter(fileName)){
fw.write("xxxxxxxxxxxxxxxxxx");
fw.write(System.lineSeparator());
fw.write("yyyyyyyyyyyyyy");
} catch (IOException e) {
}
}
synchronized static void addRecord(long value){
record.add(value);
}
static void startCase(int tCount,int reCall, Runnable runnable, List<Thread> threads,String aaa) throws InterruptedException {
long sum=0;
while(reCall-->0){
threads.clear();
record.clear();
countdown = new CountDownLatch(tCount);
for(int i=0;i<tCount;i++){
Thread t = new Thread(runnable);
t.setName(aaa+"-"+i);
threads.add(t);
}
threads.parallelStream().forEach(t->t.start());
countdown.await();
long cost = record.stream().reduce(0L,(x,y)->Long.sum(x,y));
sum = Long.sum(sum,Math.round(cost));
}
System.out.println(sum);
}
static void writeResult() {
try {
while (true) {
long value = dataQ.take();
caseWriter.write(value + System.lineSeparator());
}
}catch (Exception e){}
}
public static void main(String[] args) throws InterruptedException, IOException {
int thCount = 20;//thread count
int inputReCall = 500;//repeat count
if(args.length>0){
thCount = Integer.valueOf(Optional.ofNullable(args[0]).orElse("30"));
}
if(args.length>1){
inputReCall = Integer.valueOf(Optional.ofNullable(args[1]).orElse("10"));
}
Files.deleteIfExists(Paths.get(fileName));
Files.createFile(Paths.get(fileName));
record = new CopyOnWriteArrayList();
List<Thread> threads = new ArrayList<>(thCount);
dataQ = new ArrayBlockingQueue<>(thCount*inputReCall);
Thread dataWriter = new Thread(SycLockTest::writeResult);
dataWriter.start();
Files.deleteIfExists(Paths.get(case1File));
Files.createFile(Paths.get(case1File));
try{
caseWriter = new FileWriter(case1File,true);
startCase(thCount, inputReCall, SycLockTest::fgh, threads, "pppppppp");
}catch(Exception e){ }finally {
if(caseWriter!=null) caseWriter.close();
}
Files.deleteIfExists(Paths.get(case2File));
Files.createFile(Paths.get(case2File));
try {
caseWriter = new FileWriter(case2File,true);
startCase(thCount, inputReCall, SycLockTest::efg, threads, "qqqqqqqqqq");
}catch(Exception e){ }finally {
if(caseWriter!=null) caseWriter.close();
}
}
}
本例中有位移运算和写文件操作。本人PC处理器是4核,java version "1.8.0_231",例子中创建20个线程,每次测试循环500遍。
其实我也想测试更多的,但是一是慢,二是再多的数据会让Excel更卡。我做了N次测试,取了几次的数据,做成简易图表。
图中Y轴单位是纳秒,同时我删掉了部分数值特别大的,否则全挤到一块了。
synchronized有更好的稳定性和性能,更多点集中在底部,普遍低于500ns,数值高的点比Lock少很多。
##然后是写文件
ReentrantLock性能相对稳定且更好,但是synchronized的点有很多点集中在底部,而ReentrantLock分散的均匀。
取第一次位运算的底部放大
synchronized
ReentrantLock
能看刚开始普遍耗时多,猜测是1.大量线程启动,2.在获得锁之前需要做运算,CPU繁忙,3竞争激烈。但是在这之后由synchronized做同步的操作都能在100~300之间完成。
众所周知,synchronized由于偏向锁等优化性能有明显提高,所以现在单纯的说synchronized性能一定差就不一定准确了。
经过这几天我的反复测试,个人觉得synchronized在轻量化的操作,比如简单运算,变量递增/减,赋值等情况有更好的性能。ReentrantLock更适用于复杂度相对高的操作,比如循环遍历,插入,IO等。
在低并发,特别同时是轻量化的操作,synchronized可能可以获得更好的性能。现实情况中虽然我们可能有几百个线程,但是大多数情况下,对于共享资源的修改,同时只有几个或者十几个,那么使用synchronized也不失为一种好选择。
例子中文件操作受IO干扰比较大,不合理,改成遍历长度100的数组并赋值会怎么样?(经简单测试,lock更快)
在线程重入的情况下两者性能如何呢?(位运算,遍历数组并赋值synchronized更快,高近3个数量级)
组合synchronized和volatile对变量的增减操作和直接使用Atomicxxxx哪个更快(本人PC上是前者快,特别是并发很小的时候)
这里不得再次提一下以上结果都是基于本人PC的测试数据。因为还有10核一台云主机,测试中发现对于本例中文件操作结果是不确定的,对于synchronized+volatile的例子是低并发比如5个线程,那是前者快,否则是后这快。
##后记
测试中的数据还可以继续挖掘,比如在某一区间内的分布等。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。