并发锁分析

2023-10-30

为什么加锁

  1. 并发会造成数据混乱

事务

  1. 原子性a
  2. 一致性c
  3. 隔离性i
  4. 持久性d

加锁的影响和优化点

  1. 线程切换的开销(缓存命中率)

    加锁失败会进行线程切换

  2. 用户态和内核态的切换开销(栈的切换/寄存器切换)

    synchronized重量锁的实现是 系统调用 会涉及到内核和用户态的切换(加锁成功和失败都会切换)

  3. cas保证原子性,避免系统调用
    cas 无系统调用

  4. 加锁粒度
    表->段->行

  5. 互斥到共享

锁存在的问题

  1. 死锁

指标和分类

  • 同步和异步(阻塞和非阻塞和带超时的阻塞)
  • 互斥和读写
  • 独占和共享
  • 公平和非公平
  • 可中断
  • 可重入
  • 分段
  • 自旋

锁的组成

  1. 共享资源 比如state 必须是所有线程共享的
  2. 临界区 产生竞争的一段代码 加锁和释放锁中间的操作(左闭右闭区间)
  3. 原子性 只有一个线程可以更改共享资源 比如state+1 state-1
  4. 可见性 volatile
  5. 互斥性

java

synchronized

对象头

https://www.cnblogs.com/hongdada/p/14087177.html

状态机

无锁->偏向锁->轻量锁(自旋锁)->重量锁
状态转换关系
在这里插入图片描述
线程1进入临界区,检测到同步对象处于无锁状态,将线程id拷贝到对象头,同步对象升级为偏向锁,线程1退出时,检查同步对象头是偏向锁,不对锁进行释放
线程1再次进入临界区,检测到偏向锁的线程id是自己的id, 获取锁成功,线程1退出时,检查同步对象头是偏向锁,不对锁进行释放
线程2进入临界区,检测到偏向锁的线程id不是自己的id, 获取锁失败,将锁升级为轻量级锁(更新同步对象头的字段 设置是否为偏向锁为否),线程2进入循环获取锁,n次(可配置 默认15)
线程1退出时,判断同步对象头不是偏向锁,要把锁释放为无锁,此时循环中的线程2,可以获取到锁,并且设置为偏向锁(此时偏向的是线程2)
如果线程2在循环15层次之后,仍然没能获取到锁,那么进入阻塞,同步对象升级为重量锁(更新同步对象头,设置锁指针为一个监视器对象),
线程3进入临界区,检测对象头,已经是轻量锁,升级锁为重量锁,

线程释放锁时,检查同步对象头,
如果是偏向锁,不做任何处理,直接退出
如果是轻量锁,要把同步对象头中的偏向线程id给去掉(检查是不是自己的),轻量锁线程id给去掉,把同步对象置为无锁状态
如果是重量锁,要把同步对象中的偏向线程id给去掉,轻量锁线程id给去掉,检查重量锁的监视器对象是否还有等待状态的线程,进行唤醒,如果没有,把重量锁的监视器指针给去掉,把锁置为无锁状态

AQS

  1. 可中断
  2. state和等待队列
  3. 基于LockSupport实现
    在这里插入图片描述
    在这里插入图片描述
    park和unpark实现线程的阻塞和唤醒,底层又是通过UNSAFE实现
  4. . 加锁失败,即cas更新共享资源state失败,加入等待队列,调用park,阻塞当前线程。
    锁释放的时候,从当前阻塞队列中获取一个或全部进行唤醒

concurrenthashmap

java7

段锁

java8

行锁
CAS原子操作 链表为空插入第一个节点时是原子操作
synchronized 插入链表非第一个节点时 使用synchronized 锁住链表头节点

mysql

表锁

myisam
update where sex = 1(sex列不是索引)

行锁

innodb
依赖索引
update where index=1 (index是索引列)

间隙锁

(负无穷,a)(a,b)(b,无穷)
update where index > 2(index是索引列)
update where index<1
update where index<1 and index ❤️

乐观锁

修改时再加锁
select 不加锁
update 加锁

悲观锁

提前加锁
select for update

分布式锁

同单体应用的锁,分布式锁也需要共享资源来实现锁,共享资源需要由第三方中间件来实现,比如redis,zookeeper,mysql等
分布式锁一般是非阻塞的
要实现阻塞,需要自旋

redis

通过 nx 参数来实现CAS互斥
通过 ex 参数实现自动过期

加锁流程

  1. client1首先加锁,发现key的值为null,设置key为1 cas(key,null,1)
  2. client2其次加锁,发现key的值为1,加锁失败 cas(key,null,1)

解锁流程

  1. client1解锁,把key设置为null,或者删除key
  2. client2重试,发现key的值为null,加锁成功
  3. key超时,redis自动清除key,client2重试,发现key的值为null,加锁成功

特性:

  1. 可以通过ex 参数来设置有效期

    优点: 保证不会产生死锁,客户端宕机之后,锁不会永久存在
    缺点: 可能会在解锁之前就失效,过期时间不好确定
    
  2. 内存操作,性能高,持久性由redis保证

  3. redis保证集群的高可用

  4. 没办法阻塞,客户端只能自旋循环检测加锁

缺点:

  1. 不能实现公平锁,先阻塞的不一定先唤醒

zookeeper

临时节点实现自动过期

临时顺序节点实现互斥

watch机制实现阻塞队列

加锁流程:

  1. client1 在创建lock1临时节点
  2. client2 发现已经存在lock1临时节点 加锁失败,创建lock2临时节点,并watch lock1
  3. client3 发现已经存在lock2临时节点,加锁失败,创建lock3临时节点,并watch lock2

解锁流程

  1. client1删除lock1临时节点,client2收到lock1节点删除事件,client2唤醒.
  2. client2宕机,事务失败,lock1临时节点删除,client2收到lock1节点删除事件,client2唤醒

mysql

  1. 悲观锁

通过 select for update 实现悲观锁

  1. 乐观锁

v e r = s e l e c t v e r f r o m x x x ;   r o w s = u p d a t e x x x w h e r e v e r = ver=select ver from xxx;   rows=update xxx where ver= ver=selectverfromxxx; rows=updatexxxwherever=ver;  
rows==0 表示更新失败,表示加锁失败  
rows>0 表示加锁成功

参考来源

  1. https://blog.csdn.net/qq_19007169/article/details/124591890
  2. JAVA 对象头分析及Synchronized锁 - hongdada - 博客园 (cnblogs.com)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

并发锁分析 的相关文章

随机推荐

  • STM32 ADC

    1 什么是ADC ADC指模数转换 模拟信号是指时间幅值均连续的信号 对其采样后得到的信号称之为离散时间信号 若再对其进行量化处理则得到数字信号 请注意对模拟信号采样得到的信号不是数字信号 仅仅是离散时间信号 详情请见数字电子电路 信号与系
  • python稳健性检验_有哪些比较好的做异常值检测的方法?

    最近很多小伙伴都比较关注异常值检测的方法 接下来小编就为大家介绍几种 希望能帮到大家 摘要 本文介绍了异常值检测的常见四种方法 分别为Numeric Outlier Z Score DBSCA以及Isolation Forest 在训练机器
  • 算法导论 第三节 分治法

    分治法 1 分 把一个大问题分成若干个小问题 即原问题的n变小 2 治 递归的解决每一个子问题 然后把这些子问题的解合并成整个大问题的解 归并排序 1 一分为二 2 递归的对每一个子数组进行排序 3 合并 线性的n时间内就可以完成 归并排序
  • vscode修改npm镜像源

    npm install g cnpm registry https registry npm taobao orgnpm install g cnpm registry https registry npm taobao org npm c
  • 内核空间和应用空间的数据拷贝(copy_to_user & copy_from_user)

    1 copy to user copy from user long copy to user void user to const void from unsigned long n long copy from user void to
  • 【C++学习笔记(五十一)】之Qt中的信号和槽机制

    一 信号和槽机制 信号和槽机制分为信号和槽函数 用于处理事件 当某个事件发生时 比如说某个按钮被点击后 它就会发出一个信号 signal 如果有对象对这个信号感兴趣 那么它就会使用连接 connect 函数 将该信号与自己的一个槽函数 sl
  • ubuntu 18.04安装pycharm及编译环境配置

    PyCharm介绍 PyCharm是一种Python IDE 其带有一整套可以帮助用户在使用Python语言开发时提高其效率的工具 比如 调试 语法高亮 Project管理 代码跳转 智能提示 自动完成 单元测试 版本控制等等 此外 该ID
  • 网络编程六--UDP服务器客户端

    写在前面 UDP User Datagram Protocol 称为用户数据报协议 是一种无连接的传输协议 UDP的主要应用在即使丢失部分数据 也不影响整体效果的场景 例实时传输视频或音频时 即使丢失部分数据 也不会影响整体效果 只是会有轻
  • 74-Maven的简单介绍和使用(70-73是项目的修改和创建)

    Maven 专家 管理项目 1 理解Maven 在以前的项目编写中 jar包管理令人头疼 SSM框架 30 的jar包 所以在项目的发展中 产生一系列的新技术Maven Gradle 高新 Maven项目对象模型 可以通过一小段的描述信息来
  • 【Python】数据加密解密技术

    Python如何加密解密 感兴趣的小伙伴可以举一下脚 我看看有多少 咳咳咳 正式开始了 今天给大家分享的是Python如何加密解密 感兴趣的小伙伴要认真学起来 前言 加密算法主要分为 哈希算法 对称加密算法 非对称加密算法 哈希算法 MD5
  • Hostname/IP doesn't match certificate's altnames

    背景 一直用Docker Toolbox on VirtualBox在Windows上开发 创建的boot2docker镜像IP地址是192 168 99 100 这两天因为工作需要 想装一个minikube 安装的时候没有启动Docker
  • JavaSE复习:集合

    一方面 面向对象语言对事物的体现都是以对象的形式 为了方便对多个对象的操作 就要对对象进行存储 另一方面 使用Array存储对象方面具有一些弊端 而Java 集合就像一种容器 可以动态地把多个对象的引用放入容器中 集合与数组的比较 首先说下
  • Web3.0是什么?带你解析Web3.0

    有人还在研究什么是元宇宙和NFT 那个概念已经过时了 据说现在流行的是Web3 0和DAO 如果你只想了解关于Web3 0的观点 那么先简明扼要的告诉你 Web3 0是未来 但就2022年人类科技水平的发展来看 你目前接触到的 向你兜售通过
  • Ubuntu最大连接数

    使用ulimit n查看连接数或ulimit a查看详细信息 1 配置 etc security limits conf sudo vim etc security limits conf 文件尾追加 hard nofile 40960 s
  • pagehelper源码分析

    pagehelper不用说 只要是用过的就会爱上他 Maven依赖
  • 最大降雨量

    import os import sys 请在此输入您的代码 第四周的降雨量最大 1 2 3 46 47 48 49 4 5 6 42 43 44 45 7 8 9 38 39 40 41 10 11 12 34 35 36 37 13 1
  • C#与C++数据类型对应表

    C 调用DLL文件时参数对应表 Wtypes h 中的非托管类型 非托管 C 语言类型 托管类名 说明 HANDLE void System IntPtr 32 位 BYTE unsigned char System Byte 8 位 SH
  • Shopify开发入门-前端保姆级教程

    Shopify开发入门 前端保姆级教程 本文旨在介绍Shopify开发入门 环境 配置等 帮助开发者配置环境 了解各个开发模式的区别及用途 已有Shopify开发经验者可退出 以免浪费你的宝贵时间 本文5k字 图片 链接 代码块较多 请耐心
  • OSPF的防环机制

    文章目录 域间防环 域内防环 有向图转化 有向图的画法 示例 SPF算法 OSPF将整个OSPF域划分为多个区域 区域内部通过拓扑信息计算路由 区域间传递路由信息 实现全网可达 OSPF防环机制主要是体现在域内防环和域间防环 域间防环 OS
  • 并发锁分析

    锁 为什么加锁 并发会造成数据混乱 事务 原子性a 一致性c 隔离性i 持久性d 加锁的影响和优化点 线程切换的开销 缓存命中率 加锁失败会进行线程切换 用户态和内核态的切换开销 栈的切换 寄存器切换 synchronized重量锁的实现是