Discuss / Java / 第三次读到这一节,才弄懂了乐观锁与悲观锁对读写影响的区别。

第三次读到这一节,才弄懂了乐观锁与悲观锁对读写影响的区别。

Topic source

前一节ReadWriteLock末尾,我一直在想,(使用悲观锁时)虽然写稀少而读频繁,读之间互不影响可以完全异步进行。但是写是需要等待读的,而读又是频繁的。例如假设一个论坛的贴子10分钟内,有10000次浏览(读)和一次回复(写),假设性能为10000次浏览需要10分钟才能进行完,那么这个回复操作需要和这10000个浏览操作一起排队,运气正常的情况下,很有可能要等8-10分钟才能开始写入操作(1/10001的机率,虽然每次分母减1,不存在优先级的情况下,cpu很快选到你也是一种幸运吧)。

而乐观锁,在读开始前实际没加锁,只是记录版本号,这意味着,无论多少读在同时进行,写都能直接开始,然后读结束时检查版本号,不一致则重新加锁并修正数据。这样一来,上面的例子就不会出现需要等待8-10分钟才能开始回复的写入操作了。

我觉得乐观锁就是为了解决上面这种情况而产生的,不知道我理解的对不对?

廖雪峰

#2 Created at ... [Delete] [Delete and Lock User]

当然不对了

一般来说写锁的优先级要高于读锁,read-write-lock假定读很多几乎不会间断,如果突然来个写锁,那么只需等当前正在读的释放读锁后,写就立刻获得写锁,其它后续读都得等,不然你想,在一直都有读的情况下,永远写不了

廖雪峰

#3 Created at ... [Delete] [Delete and Lock User]

乐观锁其实不上锁,只检查版本号,它的目的是把read-write-lock的read加读锁这一步给去了,因为绝大多数情况下没有写,不需要加读锁

谢谢老师解答,明白了,乐观锁的作用就是在大部分情况下去除加读锁这一步(因为加锁比较消耗性能?)。

百度了下,乐观锁和悲观锁好像也不单单是性能那么简单的区别

十号_风球

#6 Created at ... [Delete] [Delete and Lock User]

“乐观锁其实不上锁,只检查版本号,它的目的是把read-write-lock的read加读锁这一步给去了,因为绝大多数情况下没有写,不需要加读锁”

十号_风球

#7 Created at ... [Delete] [Delete and Lock User]

接上。@廖雪峰,既然绝大多数情况下没有读写冲突,那么加读锁也不会有很大的影响呀?

1 楼说的对也不对,如果是公平的读写锁的确会有这样的问题,但是 Java 对于 ReadWriteLock 的读锁的抢锁的策略做了限制,当阻塞队列中第一个线程是一个写线程的时候,那读锁就不会参与抢锁,而是直接阻塞,这在一定程度上解决了写锁‘饿死’的问题,JDK8 中的 StampedLock 进一步解决了写锁被读锁阻塞的问题,在‘乐观锁’的读锁状态下,写锁可以直接进行获取锁,不用进行抢锁,而且 StampLock 对于抢锁失败阻塞造成频繁的上下文切换也进行了优化,线程在抢锁时(底层使用 CAS 实现)如果失败不会直接阻塞,而是通过‘自旋’不断尝试获取锁,直到尝试次数达到上限(单核CPU为0,多核为2^16)才会进行阻塞,等待被唤醒,唤醒后依旧进行自旋,如果依旧无法获得锁,那么再阻塞等待被唤醒。。。


  • 1

Reply