大家周末好!
线程安全和锁这一块一直是我想理解清楚的问题,今天看了 https://www.v2ex.com/t/574995#reply13 的问题,再次引发了我对 java 锁这块知识的疑问。
我从网络上得知,使用 synchronized 包裹方法来实现悲观锁,这是重量级的,是性能不高的。使用 CAS 实现乐观锁,这是轻量级的,一定场合下性能高于前者。
所以我就想自己测下验证下这个结论,但是验证过程中遇到了和结论不匹配的结果,所以希望请教下大家
我的示例代码如下
public class Demo1 {
private static final Sheep sheep = new Sheep(0);
static class Sheep{
private AtomicInteger size;
public Sheep(int size) {
this.size = new AtomicInteger(0);
}
public AtomicInteger getSize() {
return size;
}
public void setSize(int size) {
this.size.set(size);
}
}
//悲观
public synchronized static void increase1() {
int value = sheep.getSize().intValue();
sheep.setSize(value + 1);
}
//乐观(CAS)
public static void increase2() {
while (true){
int expect = sheep.getSize().intValue();
if(sheep.getSize().compareAndSet(expect,expect+1)){
break;
}
}
}
}
我使用如下代码来做耗时测试:
//10 个线程模拟 10 个用户,它们同时调用递增方法修改 size 属性,记录每次的耗时结果
public static void main(String[] args) throws InterruptedException{
long start = System.currentTimeMillis();
long end = 0;
ExecutorService executor = Executors.newFixedThreadPool(10);
int callTime = 10000000;
CountDownLatch countDownLatch = new CountDownLatch(callTime);
for(int i=0; i<callTime; i++) {
executor.execute(new Runnable() {
@Override
public void run() {
increase1();
//increase2();
countDownLatch.countDown();
}
});
}
countDownLatch.await();
end = System.currentTimeMillis();
executor.shutdown();
System.out.println("调用次数:" + sheep.getSize().intValue());
System.out.println("调用耗时: " + (end - start) );
}
我的记录结果如下:
* 总共操作 1000w 次,两者的耗时差不多,记录了 5 次运行的耗时:
* 悲观递增耗时记录(毫秒):6923,7030,6987,7353,7125
* 乐观递增耗时记录(毫秒):7149,7044,6937,6777,6717
*
* 总共操作 5000w 次,两者的耗时差不多,记录了 5 次运行的耗时:
* 悲观递增耗时记录(毫秒):49843,49807,51404,49143,49543
* 乐观递增耗时记录(毫秒):49762,48950,49376,48918,50324
*
* 测试环境是 IDEA,跑 main 方法
可以看到,这个案例下,悲观锁和 CAS 乐观锁在效率上没有很大的区别!为什么在这个案例下这两种实现在效率上没有区别呢?
这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。
V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。
V2EX is a community of developers, designers and creative people.