大家周末好!
线程安全和锁这一块一直是我想理解清楚的问题,今天看了 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 乐观锁在效率上没有很大的区别!为什么在这个案例下这两种实现在效率上没有区别呢?
