什么是锁:
多线程访问同一个资源,就好像上厕所一样,谁先挤进去就得把门锁上。这个“锁”是所有人共享的,但是每个人的钥匙是不一样的。每个人都要自己加锁(实际上是内核设置锁的性质),拿锁则是问管理员(内核)要。所以我们关注的是不同种类的上厕所者,加的是什么锁。
互斥锁和自旋锁是很多高级锁的基类
互斥锁加锁成功后,会让其他拿锁的线程释放cpu进入睡眠。意味着线程会在睡着和醒来之间切换两次。
这就是开销,时间成本很小,但是加锁时间有可能比执行的代码还长。
自旋锁则会让拿锁的线程进入忙等待状态,意味着没有线程切换成本。而且自旋锁 和忙等待都是通过硬件级别的指令实现的,开销更小。
线程忙等待的时候 和 被锁住的代码执行时间是成正比的。
读写锁
由读锁和写锁两个部分组成,如果只读共享资源就加读锁,修改就加写锁。
所以读写锁适用于能够明确区别读写的场景。
那么这是怎么实现的?
- 当写锁没有被人加上的时候,也就是没人要泄了,大家都可以去厕所里面看马桶。但是一旦有人要泄了,就不允许来看。更不允许进来。
所以说写锁是独占锁,因为任何时刻只有一个线程持有,类似互斥锁和自旋锁,而读锁是共享锁,因为读锁可以被多个线程持有。

写优先锁
顾名思义,优先写线程,泄的先上。上一批已经在厕所里的人我可以等一会,但是后来的不准插队。
读优先锁对于读并发性更好,但也不是没有问题。
试想一下,如果一直有读线程获取读锁,那么写线程将会很饥饿。
写优先同理。
于是乎
公平读写锁
这种锁一看就很公平,比较简单的实现是:用队列把获取锁的线程排队。
其实就是原来的读写锁,读或者写都有一个能插队,现在我不让你插队。
乐观锁和悲观锁
前面提到的 互斥、自旋、读写,都实现了进厕所关门认为不关门的话会有很可怕的后果,所以都是悲观的。
那什么是乐观的呢:我先上,我也不关门,你要上就上。我等会再来。那么问题来了:
如果乐观锁憋不住了怎么办?岂不是要拉一裤裆。
所以如果乐观锁重写成本*重写概率 大的话,就会有可怕的后果。当然是自己承担了。
因此,乐观锁全程没有加锁,也叫无锁编程。
比如:在线文档
我们知道在线文档是支持多人同时编辑的,编辑完提交后再查看是否有冲突发生。
怎么算是打架了? 举个栗子:牛马在浏览器编辑文档,出生在同样的地方编辑还提交了,这个时候牛马提交的时候就会打架。你怎么占了我的坑?
服务端冲突验证:
- 由于冲突概率低,先让用户编辑,但是浏览器在下载文档的时候会记录服务端返回的文档版本号。
- 当用于提交修改时候,发给服务端的请求会带上原始文档版本号,服务器收到后将它与当前版本号进行比较,如果版本号一致则修改成功,否则提交失败。
实际上SVN和Git也使用了乐观锁思想,先让用户编辑代码,然后提交的时候,通过版本号来判断是否产生了冲突,发生了冲突的地方需要你自己判断后重新提交。
乐观锁虽然去除了加锁解锁的操作,但是一旦发生冲突,重试的成本非常高,所以只有在冲突概率非常低,且加锁成本非常高的场景时,才考虑使用乐观锁。
789

被折叠的 条评论
为什么被折叠?



