面试常问 乐观锁 & 悲观锁 、自旋锁 & 互斥锁 ?诸君听我一言

乐观锁和悲观锁并非一种真实存在的锁,而是一种设计思想,乐观锁和悲观锁对于理解后端多线程和数据库来讲相当重要,那么本篇文章就来详细探讨一下这两种锁的概念以及实现方式。web

我就不喜欢看人说什么“Java的乐观锁和悲观锁”,思想还要分是谁是谁的嘛。。。数据库

乐观锁

在这里插入图片描述
乐观锁,你看它名字就知道,把事情想得很单纯,它总认为资源和数据不会被别人所修改,因此读取不会上锁,可是乐观锁在进行写入操做的时候会判断当前数据是否被修改过。可使用版本号等机制。后端

乐观锁多适用于多度的应用类型,这样能够提升吞吐量。多线程

实现

  1. 使用自增加的整数表示数据版本号。
    在这里插入图片描述
    若是这双写互不干扰,男的取出,版本号为0,男的写入,版本号+1;女的取出,版本号为1,女的写入,版本号为2。
    若是这双写相互干扰了,男的取出,版本号为0;男的还没写入,女的取出,版本号为0;男的写入,版本号为1;女的写入,发现版本号不匹配,则写入失败,应该从新读取金额数和版本号。并发

  2. 使用时间戳来实现.svg

悲观锁

在这里插入图片描述

悲观锁是一种悲观思想,它总认为最坏的状况可能会出现,它认为数据极可能会被其余人所修改,因此悲观锁在持有数据的时候总会把资源 或者 数据 锁住,这样其余线程想要请求这个资源的时候就会阻塞,直到等到悲观锁把资源释放为止。传统的关系型数据库里边就用到了不少这种锁机制,好比行锁,表锁等,读锁,写锁等,都是在作操做以前先上锁。悲观锁的实现每每依靠数据库自己的锁功能实现。性能

实现

可使用数据库的锁机制。优化

乐观锁 VS 悲观锁

只能说,各有千秋吧。操作系统

乐观锁适用于写比较少的状况下,即冲突真的不多发生的时候,这样能够省去了锁的开销,加大了系统的整个吞吐量。但若是常常产生冲突,这样反却是下降了性能,因此这种状况下用悲观锁就比较合适。线程

悲观锁会形成访问数据库时间较长,并发性很差,特别是长事务。
乐观锁在现实中使用得较多。

上面那个例子放在这里就变成了:一方拿到锁以后,另外一方就等着,知道一方将锁释放,另外一方继续操做。


自旋锁 & 互斥锁

自旋锁和互斥锁嘛,一直在用的,不过之前只是简单的叫它们:锁。原来人家有名字的啊。

wait() 晓得不?timewait()晓得不?

互斥锁:阻塞等待
自旋锁:等两下就去问一声:好了不?我很急啊!好了不?你快点啊。。。哈哈哈哈哈

自旋锁的原理比较简单,若是持有锁的线程能在短期内释放锁资源,那么那些等待竞争锁的线程就不须要作内核态和用户态之间的切换进入阻塞状态,它们只须要等一等(自旋),等到持有锁的线程释放锁以后便可获取,这样就避免了用户进程和内核切换的消耗。

由于自旋锁避免了操做系统进程调度和线程切换,因此自旋锁一般适用在时间比较短的状况下。因为这个缘由,操做系统的内核常用自旋锁。可是,若是长时间上锁的话,自旋锁会很是耗费性能,它阻止了其余线程的运行和调度。线程持有锁的时间越长,则持有该锁的线程将被 OS(Operating System) 调度程序中断的风险越大。若是发生中断状况,那么其余线程将保持旋转状态(反复尝试获取锁),而持有该锁的线程并不打算释放锁,这样致使的是结果是无限期推迟,直到持有锁的线程能够完成并释放它为止。

解决上面这种状况一个很好的方式是给自旋锁设定一个自旋时间,等时间一到当即释放自旋锁。适应性自旋锁意味着自旋时间不是固定的了,而是由前一次在同一个锁上的自旋时间以及锁拥有的状态来决定,基本认为一个线程上下文切换的时间是最佳的一个时间。


长尾流量优化

刷一下就过去了,肯定不收藏一下吗?

大家长得这么好看,不顺手把关注点上吗?嘿嘿
在这里插入图片描述
在这里插入图片描述