前言
锁策略学习思维导图:
1.常见锁策略
① 乐观锁和悲观锁
● 它们是根据锁冲突的预测,如果预测锁冲突比较小,那就是乐观锁,反之,就是悲观锁.
● 举个例子:高考前夕,我总觉得高考题会很难,然后拼命做各种科目的题,全副武装的去应对高考,而我妈则觉得高考只是人生的一个阶段而已,让我放平心态,顺其自然就好了.在这个例子中,我就相当于一个悲观锁,预测高考题的难度大不大类比于预测锁冲突大不大,我妈就相当于一个乐观锁.针对不同的锁,做出的事情也会不一样.
② 轻量级锁和重量级锁
● 轻量级锁的加锁解锁操作过程快且高效,而重量级锁则相反.就跟我们提东西上楼梯一样,轻的东西肯定上楼快一些,整个过程就会短一些,但是我们提的东西如果很重,那上楼就会相对于慢一些,那么整个过程就会长.
③ 自旋锁和挂起等待锁
● 当遇到锁冲突的时候,自旋锁会一直忙等,不停尝试加锁,很消耗CPU资源,但是这样也有一个好处,那就是当锁被释放,能第一时间获取到锁,它是基于纯用户态实现的,无需太多时间,而挂起等待锁则基于内核机制实现,时间会更长,它不忙等,在遇到锁冲突后,可以去做别的工作,也因为如此,它不能在锁释放后,第一时间获取到锁.
● 举个例子: 我追帅哥的时候,等到相处一段时间了,跟他告白了,但是他拒绝我了,说他暂时不想谈恋爱,嘤嘤嘤我脸皮厚,每天照样跟他发信息,这样等他想谈恋爱的时候,我就能及时的知道,让他跟我在一起.还有一种选择就是他拒绝我之后,我就去努力提升自己,让自己变得更优秀,等他什么时候想谈恋爱,我再去告白,但是这样我就不能及时知道他什么时候才想谈恋爱了.
④ 互斥锁和读写锁
synchronized就是互斥锁,只是单纯加锁解锁操作,只针对一个锁进行操作,但是读写锁分为读锁和写锁,操作有三步,先给读加锁,然后给写加锁,最后释放锁.
注: 针对同一变量:
● 读锁和读锁之间没有锁冲突,所以无需等待.
● 读锁和写锁之间和写锁与写锁之间就会有锁冲突,需要等待.
⑤ 可重入锁和不可重入锁
连续对同一锁对象进行加锁,如果出现死锁状态,那么就是不可重入锁,反之则是可重入锁.
注:如果大家对死锁不太熟悉,可以看看我这篇博客:死锁知识点分享
⑥ 公平锁和非公平锁
公平锁和非公平锁相对好理解一点,就是是否遵循先来后到的原则.被男神拒绝之后(追我男神的人肯定超多),当男神想谈恋爱了,如果我跟他时间相处的更久,对他付出的最多,按照公平锁的规则,男神应该先跟我在一起,但是如果是非公平锁的话,就不一定是我了,追男神的女生都有平等机会和男神在一起.
2.cas是什么?aba问题是什么?如何解决?
① cas是什么
cas全名compare and swap,即"比较和交换",它是CPU支持无锁的一条原子的且线程安全的CPU指令,它会比较内存A和寄存器B中的值是否相等,如果相等,就将内存A和寄存器C的值交换.
注:基于cas的操作实现有AtomInteger类,让++,--操作都线程安全,SpinLock类,反复检查当前锁释放被释放.
② aba问题
cas指令只能判断寄存器B与内存A中的值是否相等,但是没办法判断寄存器B中的值是否发生改变.如果cas指令判断的是寄存器B已经修改后的值与内存A相等就可能会出现错误.我转账给朋友A,原本我账户有1000元,转账给朋友500应该剩500,刚开始线程A中的cas指令判断我还有1000块然后扣掉了我账户的500块,突然朋友B给我又转了500过来,我的余额又变成了1000,那么线程B的cas指令就判断出我的账户还是1000,又扣了我500,就相当于我被扣了两次钱.这就产生了aba问题.
③ 如何解决aba问题
很简单,每次对要修改的值给它编个版本号,那么用cas指令除了判断寄存器中的值是否相等外,还要判断变量的版本号是否大于当前值的版本号,.才能执行对应操作.
在上述例子中,当线程B对我进行账户判断的时候,经历了一系列操作后,我当前的金额版本号肯定比它先前获取到的版本号要大,所以它是无法对我的账户进行扣款操作的.
3.synchronized的其它优化操作
① 锁消除
简单的说就是非必要不加锁,比如说,在非多线程环境下,还不断进行加锁解锁操作(本身会浪费一定的资源)的话,这种资源浪费是没必要的,所以一般编译器和JVM都会判断锁是否可以消除,如果可用消除的话,就将锁消除.
② 锁粗化
这个知识点涉及到锁的粒度,锁的粒度指synchronized修饰的代码块的代码量,一般都要求锁的粒度要尽可能的小(让串行执行的代码少一点,并行执行的代码就多一点),但是当没有其它线程来跟我们抢锁的时候,为了减少解锁用的时间,我们可以选择将锁粗化,将锁的粒度变大.
注:在前篇博客中分享了synchronized属于什么锁:synchronized学习大总结
分享完毕,有不对的地方欢迎大家一起学习指点一下我哦~