当前位置: 首页 > news >正文

锁策略、原子编程CAS 和 synchronized 优化过程

前言

锁冲突:两个线程获取一把锁,一个线程阻塞等待,一个线程加锁成功。

目录

前言

一、锁策略

(一)乐观锁和悲观锁

(二)重量级锁和轻量级锁

(三)自旋锁和挂起等待锁

(四)读写锁

(五)公平锁和非公平锁

(六)可重入锁和不可重入锁

二、原子编程CAS

(一)实现原子类

(二)实现自旋锁

(三)CAS的ABA问题

三、synchronized 优化过程

结语


一、锁策略

(一)乐观锁和悲观锁

根据加锁之前对锁冲突概率的预测,预定工作的多少!

乐观锁:预测该场景中,不太出现锁冲突的情况,后续做的工作更少。

悲观锁:预测该场景中,非常容易出现锁冲突的情况,后续做的工作更多。

synchronized初始使用乐观锁策略,当发现锁竞争比较频繁时,就会自动切换成悲观锁策略。

(二)重量级锁和轻量级锁

加锁之后,考虑实际的锁的开销。

重量级锁:加锁的开销越大,花的时间越多,占用系统资源多。

轻量级锁:加锁的开销越小,花的时间越少,占用系统资源少。

synchronized开始是一个轻量级锁,如果锁冲突比较严重,就会变成重量级锁。

(三)自旋锁和挂起等待锁

自旋锁:轻量级锁的一种典型实现。在用户态下,通过自旋的方式(while循环),实现类似于加锁的效果,消耗一定的CPU资源,但可最快速度拿到锁。

挂起等待锁:重量级锁的一种典型实现。通过内核态,借助系统提供的锁机制,当出现锁冲突时,会牵扯到内核对于线程的调度,使冲突的线程出现挂起(阻塞等待),消耗的CPU资源少,但无法保证第一时间拿到锁。

synchronized中的轻量级锁策略大概是通过自旋锁实现的;

synchronized中的重量级锁策略大概是基于系统的互斥锁实现的。

(四)读写锁

将读操作和写操作分开了。如图:

synchronized不是读写锁。

(五)公平锁和非公平锁

公平锁:遵循先来后到的道理。

非公平锁:看起来概率相等,实际不公平(每个线程的阻塞时间不一样)。

synchronized是非公平锁。

(六)可重入锁和不可重入锁

可重入锁:如果一个线程争对一把锁连续加锁两次,不会出现死锁的情况。

不可重入锁:如果一个线程争对一把锁连续加锁两次,会出现死锁的情况。

synchronized是可重入锁。

二、原子编程CAS

CAS本质上是一种无锁编程,将某个寄存器中的值和内存中的值进行比较,如果相等则进行交换

(一)实现原子类

可以使用 自增/自减/自增任意值/自减任意值 实现计数、统计这类场景中。

常用的有:AtomicInteger、AtomicLong

加锁保证线程安全是通过锁避免出现穿插;但是CAS保证线程安全是借助CAS识别当前是否出现穿插的情况,如果没有穿插就是安全的,穿插了就重新读取内存的最新值,再次尝试修改。

(二)实现自旋锁

获取当前线程的引用,判断其是否进行了加锁。加锁了就自旋等待,没有加锁就将内置变量owner设为当前尝试加锁的线程。

(三)CAS的ABA问题

CAS是根据判断内存和寄存器中的值是否相等来进行判断其是否发生改变。但是如果这两者都发生了从A变成B,又从B变成了A的情况,那我们的CAS就会判断错误,从而导致A再次发生变化。

为了解决这类ABA问题,就引入了版本号,判断版本号是否相等即可

数据每修改一次,版本号就增加一次,故此原先的A和后来的A的版本号不同,判断结果也就会判断他们不同,不会使其发生改变。

三、synchronized 优化过程

synchronized不是一开始就是对我们的代码块处于进行加锁的状态。synchronized的改变是一个自适应的过程: 无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁。

偏向锁:不是真的加锁,而只是做了一个标记。如果有别的线程来竞争锁了,就会真的加锁如果没有其他线程竞争锁,就会始终都不会真的加锁

synchronized还有一些其他的优化操作

锁消除:编译器会自动的判断你当前的代码是否有必要加锁。如果你写了锁,但实际上没必要加锁就会把锁自动删掉。

锁粗化:关于“锁的粒度”。如果加锁操作里包含的实际执行语句多,就认为锁的粒度越大。


结语

这篇博客如果对你有帮助,给博主一个免费的点赞以示鼓励,欢迎各位🔎点赞👍评论收藏⭐,谢谢!!!

http://www.lryc.cn/news/144498.html

相关文章:

  • 【WINAPI】文件读写操作问题
  • 【LeetCode-中等题】148. 排序链表
  • Ceph EC pg backfill run
  • 腾讯云服务器地域怎么选?广州上海北京?
  • Apple Configurator iphone ipad 设备管控 描述文件使用方法
  • Linux 管道(pipe)用法
  • 元素隐式具有 “any“ 类型,因为类型为 “string“ 的表达式不能用于索引类型
  • 34、springboot切换内嵌Web服务器(Tomcat服务器)与 生成SSL证书来把项目访路径从 HTTP 配置成 HTTPS
  • 3种CSS实现背景图片全屏铺满自适应的方式
  • M1 Pro 利用docker 搭建pytho2的开发环境,以vscode连接开发为例
  • MySQL概述,架构原理
  • Three.js实现模型,模型材质可拖拽效果 DragControls
  • 机器学习笔记之优化算法(二十)牛顿法与正则化
  • 【Go 基础篇】深入探索:Go语言中的切片遍历与注意事项
  • 一些经典的SQL语句
  • 〔018〕Stable Diffusion 之 批量替换人脸 篇
  • Unity字符串性能问题
  • 深入浅出SSD:固态存储核心技术、原理与实战(文末赠书)
  • 关于layui+php,三级联动-编辑回显的问题。
  • lua的函数
  • pytorch/tensorflow 直接给张量中的某个位置的值赋值,操作不可导。
  • 如何使用CSS实现一个平滑滚动到页面顶部的效果(回到顶部按钮)?
  • 【RuoYi移动端】uniApp导入和引用uView2.0插件
  • etcd 备份还原
  • LInux之chrony服务器
  • 《Flink学习笔记》——第七章 处理函数
  • Nacos基础(3)——nacos+nginx 集群的配置和启动 端口开放 nginx反向代理nacos集群
  • 传承精神 缅怀伟人——湖南多链优品科技有限公司赴韶山开展红色主题活动
  • 安全知识普及-如何创建一个安全的密码
  • Lua基础知识