栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

各种锁的策略和锁优化

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

各种锁的策略和锁优化

一、锁的分类

1)乐观锁 vs 悲观锁

乐观锁:多个线程竞争一把锁的概率会很低

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

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

2)读写锁

把加锁操作分成了两种。

a)读锁

b)写锁

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

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

3)重量级锁 vs 轻量级锁

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

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

4)挂起等待锁 vs 自旋锁

  挂起等待锁 表示 当前获取锁失败之后,对应的线程就要在 内核中挂起等待(放弃 CPU,进入等待队列)

  需要在锁被释放之后由操作系统唤醒【通常都是重量级锁】

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

自旋锁的效率更高,但是会浪费一些 CPU 资源(自旋过程相当于 CPU 在空转)

一旦线程挂起,下次啥时候能被调度上,就不可预期了(时间可能很久,能够达到ms级别,极端情况下可能是 s 级)

Windows/Linux 这样的系统,调度过程都是没那么快的~~~调度时间不可预期】

5)公平锁 vs 非公平锁

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

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

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

要想实现公平锁,就需要付出一些额外的代价

6)可重入锁

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

当锁的持有这正好就是新的锁的申请者,此时就特殊处理下让加锁操作成功即可。

死锁的典型场景:

1.一个线程针对一把锁连续加两次

2.两个线程针对两把锁分别加锁

3.N个线程针对 N 把锁分别加锁

二、锁优化的策略

锁优化就是一些提高锁的效率的策略,下面以synchronized为例,来介绍优化操作。

1.锁消除: 编译器 + JVM  会根据代码运行的情况智能判定当前的锁是否必要,如果不必要,就直接把加锁的代码忽略。

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

1和2的优化思路相同:能不加锁就不加锁

3.自旋锁:真的有多个线程竞争锁的时候,偏向锁状态被消除,此时线程使用自旋锁的方式来尝试进行获取锁。自旋锁能保证让其他想竞争锁的状态尽快获取到锁,付出了一定的CPU 资源

4.膨胀锁 (无奈之举,严格上说,不能算优化)

   当锁竞争更加激烈,此时就会从自旋锁状态膨胀成重量级锁(挂起等待锁)

5.锁粗化

如果一段逻辑中,需要多次加锁解锁,并且加锁解锁的时候没有其他线程来竞争,此时就会把多组加锁操作,合并到一起。

粗化就是把 多组加锁解锁 操作合并成一组。每次加锁解锁操作,都有开销,减少加锁的次数,就能提高效率了。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/301312.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号