锁策略

1.锁策略

1.1 乐观锁 vs 悲观锁

乐观锁:世界大概率是和平的,多个线程竞争一把锁的概率会很低

悲观锁:世界大概率是出问题的,多个线程竞争一把锁的概率会很高,恢复出更多的成本来进行锁冲突的处理

两种想法没有优劣之分,要根据具体场景来进行使用

1.2 读写锁

把加锁操作分成了两种:读锁、写锁

读锁和读锁之间是没有互斥的(不存在锁竞争),读锁和写锁、写锁和写锁之间才进行互斥

如果某个场景下“一写多读”,使用读写锁效率就很高

只进行读数据,若果多个线程同时读取一个数据,不会造成线程不安全的情况,只有修改同一个数据才会线程不安全

1.3 重量级锁 vs 轻量级锁

加锁需要保证原子性,原子性功能来源自硬件(硬件提供了相关的原子操作的指令,操作系统封装一下成为原子操作的接口,应用程序才能使用这样的操作)

在加锁过程中,如果整个加锁逻辑都是依赖于操作系统内核的,此时就是重量级锁(代码在内核中的执行开销会比较大),对应的,如果大部分操作都是用户自己完成,少数操作由内核完成,这种就是轻量级锁

1.4  挂起等待锁 vs 自旋锁

挂起等待锁表示获取锁失败之后,对应的线程就要在内核中挂起等待(放弃 CPU ,进入等待队列),需要在锁被释放之后由操作系统唤醒【通常都是重量级锁】

自旋锁表示在获取锁失败之后,不会立刻放弃 CPU ,而是快速频繁的再次询问锁的持有状态,一旦锁被释放了,就能立刻获取到锁【通常都是轻量级锁】

自旋锁的效率更高,但是会浪费一些 CPU 资源(自选过程相当于 CPU 在空转),线程能够获取CPU 是一件来之不易的事情,一旦线程挂起,下次什么时候能被调度上,就不可预期了,(时间可能很久,能够达到 ms 级,也有可能是 s 级)

Windows、Linux 这样的系统,调度是没那么快的,调度时间不可预期

1.5 公平锁 vs 非公平锁

如果多个线程都在等待一把锁的释放,当锁释放之后,恰好又来了一个线程也要获取锁

公平锁:能保证之前先来的线程优先获取锁

非公平锁:新来的线程直接获取到锁,之前的线程还在等待

1.6 可重入锁

一个线程针对一把锁,连续加锁两次,不会死锁,这就是可重入锁

若果锁记录自己是被谁持有的,就可以进行特殊判定了,当锁的持有至正好就是新的锁的申请者,此时就特殊处理下让加锁操作成功即可。

死锁的经典场景:

  1. 一个线程,一把锁,连续加锁两次
  2. 两个线程,两把锁,相互获取对方的锁
  3. N个线程,N把锁,哲学家就餐问题

可重入锁就是为了解决第一种场景

 

2. CAS compare and swap

硬件设备提供的一种基础指令,基于这样的基础指令,就可以实现一些特殊的功能(实现锁)

2.1应用场景

无锁编程

不使用锁,而是直接用 CAS 来保证线程安全

例如,针对某个变量进行 ++ 操作

 

2.2 CAS缺陷

2.2.1ABA问题

该线程看到 size 是 0

  1. 该 size 没有变过
  2. size 变了,以后又被其他线程改成 0 了

要想解决 ABA 就需要引入额外的信息,给变量加一个版本号,每次进行修改,就递增版本号

 

3.锁优化

以 sychronized 为例

优化操作是 编译器+JVM 两者配合进行的

3.1 锁消除

编译器+JVM 会根据代码运行的情况智能判定当前的锁是否必要,如果不必要,就直接把锁的代码干掉

想上面的代码,每次执行都会涉及加锁和解锁操作,事实上当前的额 StringBuffer 只是在一个线程中使用,不涉及线程安全问题

3.2 偏向锁

第一个尝试加锁的线程,不会真的加锁,而是进入偏向锁状态。(很轻量的标记),直到其他线程也来竞争这把锁,才会取消偏向锁状态,真正进行加锁

 

3.3 自旋锁

真的有多个线程经整改锁的时候,偏向锁状态被消除,此时线程使用自旋锁的方式来尝试进行获取锁。

 

3.4 膨胀锁(无奈之举)

严格上锁不能算优化,当锁竞争更加激烈,此时就会从自旋锁状态膨胀成重量级锁(挂起等待锁)

 

3.5 锁粗化

如果一段逻辑中,需要多次解锁解锁,并且解锁时没有其他线程来竞争,此时就会把锁粗化

 

优化前:

      append                      append                      append                      append                      append

加锁          解锁          加锁          解锁          加锁          解锁          加锁          解锁          加锁          解锁

优化后:

     append                      append                      append                      append                      append

加锁                                                                                                                                                    解锁

 

粗化就是把多组加锁解锁合并成一组,每次加锁解锁操作,都有开销,减少加锁的次数,就能提高效率了,就比如我们要搬家,没搬一个东西出门就开一次门、锁一次门,N个行李就重复 N 次开门锁门操作,为了而提高效率可以把门一直开着,搬完了行李再锁门

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值