【java】Java中的锁

news2024/11/25 0:31:47

文章目录

  • 前言
  • 一、悲观锁
  • 二、乐观锁
  • 三、自旋锁
    • 原理
    • 自旋锁优缺点
      • 优点
      • 缺点
    • 自旋锁时间阈值(1.6 引入了适应性自旋锁)
    • 自旋锁的开启
  • 四、可重入锁(递归锁)
  • 五、读写锁
  • 六、公平锁
  • 七、非公平锁
  • 八、共享锁
  • 九、独占锁
  • 十、轻量级锁
  • 十一、重量级锁
  • 十二、偏向锁
  • 十三、分段锁
  • 十四、互斥锁
  • 十五、同步锁
  • 十六、死锁
  • 十七、锁粗化
  • 十八、锁消除
  • 十九、synchronized
  • 二十、Lock和synchronized的区别
  • 二十一、ReentrantLock 和synchronized的区别
  • 总结

前言

一、悲观锁

举个生活中的例子,假设厕所只有一个坑位了,悲观锁上厕所会第一时间把门反锁上,这样其他人上厕所只能在门外等候,这种状态就是「阻塞」了。

认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。

  • synchronized关键字和Lock的实现类都是悲观锁

  • 适用场景:适合写操作多的场景,先加锁可以保证写操作时数据正确。
    在这里插入图片描述

二、乐观锁

举个生活中的例子,假设厕所只有一个坑位了,乐观锁认为:这荒郊野外的,又没有什么人,不会有人抢我坑位的,每次关门上锁多浪费时间,还是不加锁好了。你看乐观锁就是天生乐观!

认为自己在使用数据时不会有别的线程修改数据或资源,所以不会添加锁。

在Java中是通过使用无锁编程来实现,只是在更新数据的时候去判断,之前有没有别的线程更新了这个数据。

如果这个数据没有被更新,当前线程将自己修改的数据成功写入。
如果这个数据已经被其他线程更新,则根据不同的实现方式执行不同的操作,比如放弃修改、重试抢锁等

  • 乐观锁可以使用版本号机制和CAS算法实现。在 Java 语言中 java.util.concurrent.atomic包下的原子类就是使用CAS 乐观锁实现的。
  • 适用场景:适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。
    在这里插入图片描述

三、自旋锁

原理

自旋锁原理非常简单,如果持有锁的线程能在很短时间内释放锁资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换进入阻塞挂起状态,它们只需自旋,等持有锁的线程释放锁后即可立即获取锁,这样就避免用户线程和内核的切换的消耗。

线程自旋需消耗 cup 的,如果一直获取不到锁,则线程长时间占用CPU自旋,需要设定一个自旋等待最大事件在最大等待时间内仍未获得锁就会停止自旋进入阻塞状态。

自旋锁优缺点

优点

自旋锁尽可能的减少线程的阻塞,这对于锁的竞争不激烈,且占用锁时间非常短的代码块来说性能能大幅度的提升,因为自旋的消耗会小于线程阻塞挂起再唤醒的操作的消耗(这些操作会导致线程发生两次上下文切换)

缺点

锁竞争激烈或者持有锁的线程需要长时间占用锁执行同步块,不适合使用自旋锁了,因为自旋锁在获取锁前一直都是占用 cpu 做无用功,同时有大量线程在竞争一个锁,会导致获取锁的时间很长,线程自旋的消耗大于线程阻塞挂起操作的消耗,其它需要 cup 的线程又不能获取到 cpu,造成 cpu 的浪费

自旋锁时间阈值(1.6 引入了适应性自旋锁)

自旋锁的目的是为了占着 CPU 的资源不释放,等到获取到锁立即进行处理

自旋执行时间太长,会有大量的线程处于自旋状态占用 CPU 资源,进而会影响整体系统的性能

JVM 对于自旋周期的选择,jdk1.5 这个限度是一定的写死的

在 1.6 引入了适应性自旋锁,自旋的时间不固定,而是由前一次在同一个锁上的自旋时间以及锁的拥有者的状态来决定,基本认为一个线程上下文切换的时间是最佳的一个时间

自旋锁的开启

JDK1.6 中-XX:+UseSpinning 开;XX:PreBlockSpin=10 为自旋次数
JDK1.7 后,去掉此参数,由 jvm 控制

在这里插入图片描述

四、可重入锁(递归锁)

可重入锁(递归锁),指的是同一线程 外层函数获得锁之后 ,内层递归函数仍然有获取该锁的代码,但不受影响,在 JAVA 环境下 ReentrantLock 和 synchronized 都是可重入锁。

对于Java ReentrantLock而言, 他的名字就可以看出是一个可重入锁。对于Synchronized而言,也是一个可重入锁。

敲黑板:可重入锁的一个好处是可一定程度避免死锁。

以 synchronized 为例,看一下下面的代码:
在这里插入图片描述

上面的代码中 methodA 调用 methodB,如果一个线程调用methodA 已经获取了锁再去调用 methodB 就不需要再次获取锁了,这就是可重入锁的特性。如果不是可重入锁的话,mehtodB 可能不会被当前线程执行,可能造成死锁。
在这里插入图片描述

五、读写锁

为了提高性能,Java 提供了读写锁,在读的地方使用读锁,在写的地方使用写锁,灵活控制
如果没有写锁的情况下,读是无阻塞的,在一定程度上提高了程序的执行效率。

读写锁分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥由 jvm 控制的,程序员只需要上好相应的锁
要求代码只读数据,可以很多人同时读,但不能同时写,可上读锁代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁Java 中 读 写 锁 有 个 接 口 java.util.concurrent.locks.ReadWriteLock , 也 有 具 体 的 实 现ReentrantReadWriteLock
在这里插入图片描述

六、公平锁

公平锁是指多个线程按照申请锁的顺序来获取锁,这里类似排队买票,先来的人先买,后来的人在队尾排着,这是公平的。
在这里插入图片描述

先到先得,先来先服务原则

七、非公平锁

非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级翻转,或者饥饿的状态(某个线程一直得不到锁)
在这里插入图片描述

后来的也可能先获得锁

在 java 中 synchronized 关键字是非公平锁,ReentrantLock默认也是非公平锁。
在这里插入图片描述

八、共享锁

共享锁是指锁可被多个线程所持有。如果一个线程对数据加上共享锁后,那么其他线程只能对数据再加共享锁,不能加独占锁。获得共享锁的线程只能读数据,不能修改数据。

在这里插入图片描述

在 JDK 中 ReentrantReadWriteLock 就是一种共享锁。

九、独占锁

独占锁是指锁一次只能被一个线程所持有。如果一个线程对数据加上排他锁后,那么其他线程不能再对该数据加任何类型的锁。获得独占锁的线程即能读数据又能修改数据。
在这里插入图片描述

JDK中的synchronizedjava.util.concurrent(JUC)包中Lock的实现类就是独占锁。

十、轻量级锁

当线程竞争变得比较激烈时,偏向锁就会升级为轻量级锁,轻量级锁认为虽然竞争是存在的,但是理想情况下竞争的程度很低,通过自旋方式等待上一个线程释放锁。

十一、重量级锁

如果线程并发进一步加剧,线程的自旋超过了一定次数,或者一个线程持有锁,一个线程在自旋,又来了第三个线程访问时(反正就是竞争继续加大了),轻量级锁就会膨胀为重量级锁,重量级锁会使除了此时拥有锁的线程以外的线程都阻塞。

升级到重量级锁其实就是互斥锁了,一个线程拿到锁,其余线程都会处于阻塞等待状态。

在 Java 中,synchronized 关键字内部实现原理就是锁升级的过程:无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁。

这一过程在后续讲解 synchronized 关键字的原理时会详细介绍。

十二、偏向锁

Java偏向锁(Biased Locking)是指它会偏向于第一个访问锁的线程,如果在运行过程中,只有一个线程访问加锁的资源,不存在多线程竞争的情况,那么线程是不需要重复获取锁的,这种情况下,就会给线程加一个偏向锁。

偏向锁的实现是通过控制对象Mark Word的标志位来实现的,如果当前是可偏向状态,需要进一步判断对象头存储的线程 ID 是否与当前线程 ID 一致,如果一致直接进入。

十三、分段锁

分段锁是一种机制: 最好的例子来说明分段锁是ConcurrentHashMap。

ConcurrentHashMap原理:它内部细分了若干个小的 HashMap,称之为段(Segment)。默认情况下一个 ConcurrentHashMap 被进一步细分为 16 个段,既就是锁的并发度。如果需要在 ConcurrentHashMap 添加一项key-value,并不是将整个 HashMap 加锁,而是首先根据 hashcode 得到该key-value应该存放在哪个段中,然后对该段加锁,并完成 put 操作。在多线程环境中,如果多个线程同时进行put操作,只要被加入的key-value不存放在同一个段中,则线程间可以做到真正的并行。

线程安全:ConcurrentHashMap 是一个 Segment 数组, Segment 通过继承ReentrantLock 来进行加锁,所以每次需要加锁的操作锁住的是一个 segment,这样只要保证每个 Segment 是线程安全的,也就实现了全局的线程安全。

在这里插入图片描述

十四、互斥锁

互斥锁是独占锁的一种常规实现,是指某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。

在这里插入图片描述

互斥锁一次只能一个线程拥有互斥锁,其他线程只有等待。

十五、同步锁

同步锁与互斥锁同义,表示并发执行的多个线程,在同一时间内只允许一个线程访问共享数据。

Java中的同步锁: synchronized

十六、死锁

死锁是一种现象:如线程A持有资源x,线程B持有资源y,线程A等待线程B释放资源y,线程B等待线程A释放资源x,两个线程都不释放自己持有的资源,则两个线程都获取不到对方的资源,就会造成死锁。

Java中的死锁不能自行打破,所以线程死锁后,线程不能进行响应。

所以一定要注意程序的并发场景,避免造成死锁。

十七、锁粗化

锁粗化就是将多个同步块的数量减少,并将单个同步块的作用范围扩大,本质上就是将多次上锁、解锁的请求合并为一次同步请求。

举个例子,一个循环体中有一个代码同步块,每次循环都会执行加锁解锁操作。

在这里插入图片描述

经过锁粗化后就变成下面这个样子了:
在这里插入图片描述

十八、锁消除

锁消除是指虚拟机编译器在运行时检测到了共享数据没有竞争的锁,从而将这些锁进行消除。
举个例子让大家更好理解。
在这里插入图片描述

上面代码中有一个 test 方法,主要作用是将字符串 s1 和字符串 s2 串联起来。
test 方法中三个变量s1, s2, stringBuffer, 它们都是局部变量,局部变量是在栈上的,栈是线程私有的,所以就算有多个线程访问 test 方法也是线程安全的。
我们都知道 StringBuffer 是线程安全的类,append 方法是同步方法,但是 test 方法本来就是线程安全的,为了提升效率,虚拟机帮我们消除了这些同步锁,这个过程就被称为锁消除。
在这里插入图片描述

十九、synchronized

synchronized是Java中的关键字:用来修饰方法、对象实例。属于独占锁、悲观锁、可重入锁、非公平锁。

  1. 作用于实例方法时,锁住的是对象的实例(this);

  2. 当作用于静态方法时,锁住的是 Class类,相当于类的一个全局锁, 会锁所有调用该方法的线程;

  3. synchronized 作用于一个非 NULL的对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列,当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。

每个对象都有个 monitor 对象, 加锁就是在竞争 monitor 对象,代码块加锁是在代码块前后分别加上 monitorenter 和 monitorexit 指令来实现的,方法加锁是通过一个标记位来判断的。

二十、Lock和synchronized的区别

  • Lock: 是Java中的接口,可重入锁、悲观锁、独占锁、互斥锁、同步锁。

    1. Lock需要手动获取锁和释放锁。就好比自动挡和手动挡的区别

    2. Lock 是一个接口,而 synchronized 是 Java 中的关键字, synchronized 是内置的语言实现。

    3. synchronized 在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而 Lock 在发生异常时,如果没有主动通过 unLock()去释放锁,则很可能造成死锁现象,因此使用 Lock 时需要在 finally 块中释放锁。

    4. Lock 可以让等待锁的线程响应中断,而 synchronized 却不行,使用 synchronized 时,等待的线程会一直等待下去,不能够响应中断。

    5. 通过 Lock 可以知道有没有成功获取锁,而 synchronized 却无法办到。

    6. Lock 可以通过实现读写锁提高多个线程进行读操作的效率。

  • synchronized的优势:

    1. 足够清晰简单,只需要基础的同步功能时,用synchronized。

    2. Lock应该确保在finally块中释放锁。如果使用synchronized,JVM确保即使出现异常,锁也能被自动释放。

    3. 使用Lock时,Java虚拟机很难得知哪些锁对象是由特定线程锁持有的。

二十一、ReentrantLock 和synchronized的区别

ReentrantLock是Java中的类 : 继承了Lock类,可重入锁、悲观锁、独占锁、互斥锁、同步锁。

  • 划重点

    • 相同点:

      1. 主要解决共享变量如何安全访问的问题

      2. 都是可重入锁,也叫做递归锁,同一线程可以多次获得同一个锁,

      3. 保证了线程安全的两大特性:可见性、原子性。

  • 不同点:

    1. ReentrantLock 就像手动汽车,需要显示的调用lock和unlock方法, synchronized 隐式获得释放锁。

    2. ReentrantLock 可响应中断, synchronized 是不可以响应中断的,ReentrantLock 为处理锁的不可用性提供了更高的灵活性

    3. ReentrantLock 是 API 级别的, synchronized 是 JVM 级别的

    4. ReentrantLock 可以实现公平锁、非公平锁,默认非公平锁,synchronized 是非公平锁,且不可更改。

    5. ReentrantLock 通过 Condition 可以绑定多个条件

总结

前面讲了 Java 语言中各种各种的锁,最后再通过六个问题统一总结一下:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/467205.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Docker实战笔记5-利用 commit 理解镜像构成

转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/details/130338433 本文出自【赵彦军的博客】 文章目录 慎用 docker commit 注意: docker commit 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但…

【软件测试】测试用例的设计

文章目录 一. 针对没有需求的案例来设计测试用例二. 针对有需求的案例来设计测试用例1. 穷举法2. 等价类3. 边界值4. 判定表法5. 场景设计法5.1 简介5.2 基本设计步骤5.3 基本流和备选流5.4 使用场景5.5 优缺点5.6 实例 6. 错误猜测法 一. 针对没有需求的案例来设计测试用例 针…

网络安全入行?来了解下网络安全从业人员类别及其工作任务

又到了每年重保期间,红蓝双方都开始进行准备蓄势待发,网络安全从业人员每年供不应求,尤其是重保期间,双方都在疯狂的招揽准备网络安全人员。那网络安全从业人员分类到底有哪些,都负责哪些具体的工作任务呢?…

基于云计算技术的B/S架构云HIS 云HIS 云HIS系统

传统的HIS经历了20多年的建设,已经从单机版发展到局域网的版本,更被深入应用到医院的各项业务活动,成为医院必不可缺的基础设施平台,724小时不间断地支撑医院运行。因此医院都十分重视信息化建设。随着医改的不断推进和医疗行业的…

前端路由hash模式以及history模式详解

文章目录 前言一、hash 模式1、hash 定义2、location 对象3、window.onhashchange 事件 二、history 模式1、history 定义2、history API3、window.onpopstate 事件4、解决history模式下页面刷新404问题 前言 在前后端分离的项目中,前端一般使用 SPA 单页面应用模式…

屌的一批,阿里P8推荐的Spring Cloud实战笔记

Spring Cloud 前世今生 Spring Cloud体系由Spring 在2015年年初推出,至今已经五年多,其子项目在不断增加与完善,其所包含的功能点已经基本满足整个微服务体系的需求。加上Spring本身强大的技术后盾和活跃的技术社区氛围,Spring C…

车载毫米波雷达中国市场有望在2025年实现30亿美元的市场规模

近日,专注于前沿科技领域的国际咨询机构ICV发布了全球车载毫米波雷达的市场研究报告,汽车毫米波(mmWave)雷达基于频率为77 GHz的电磁波,具有高精度和准确性,可用于目标检测。这种技术有着广泛的应用&#x…

解决方案丨票据集中在集团总部处理,如何解决实物票据管理难?

目前越来越多的企业都成立了财务共享中心,通过统一财务中心可以进行集中式、标准化、统一化管理,提升财务运营水平与效率、降低企业的整体运作成本、集团战略发展支撑。 如何确保财务共享中心稳健和高效运营,是很多企业建立共享中心后面的难…

【Leetcode每日一刷】动态规划:931. 下降路径最小和

博主简介:努力学习的22级计科生博主主页: 是瑶瑶子啦所属专栏: LeetCode每日一题–进击大厂 目录 一、动态规划套路二、分析1、dp数组含义2、确定递推公式(递推函数的实现)3、dp数组初始化(base case)4、遍历顺序 解题…

React面向组件编程

React面向组件编程 基本理解和使用组件三大核心属性1: state效果理解强烈注意 组件三大核心属性2: props效果理解作用编码操作 组件三大核心属性3: refs与事件处理效果理解编码2.4.4. 事件处理 收集表单数据效果理解 组件的生命周期(重要)效果理解生命周期流程图(旧…

《庖丁解牛Linux内核分析》课后实验:实验一:反汇编分析

实验内容 参照第 1.3 节,将如下 C 语言代码汇编成“.s”文件,并分析“.s”文件中的汇编代码 的执行过程,其中重点关注 EBP/ESP 寄存器、EAX 寄存器、EIP 寄存器和函数调用堆栈空 间在汇编代码的执行过程是如何变化的。 int g(int x) { return…

省钱!NewBing硬核新玩法;手把手教你训练AI模特;用AI替代同事的指南;B站最易上手AI绘画教程 | ShowMeAI日报

👀日报&周刊合集 | 🎡生产力工具与行业应用大全 | 🧡 点赞关注评论拜托啦! 🤖 『NewBing 的2种硬核新用法』阅读文档并回答问题 & AI绘图 社区同学分享了两种NewBing的新用法,不仅准确高效&#x…

2023年湖北省武汉安全员ABC报名时间考试通过率如何?启程别

2023年湖北省武汉安全员ABC报名时间考试通过率如何?启程别 都2023年了,建筑企业想要报考三类人员,不知道在哪里报名,具备什么样的条件才可以报考建筑安全员abc,什么时间报名考试等等一系列问题,启程别来给你们细细说明…

即时通讯源码自己开发好还是对接云服务好

随着科技的飞速发展,通信技术也日新月异。与此同时,即时通讯应用也越来越受欢迎。许多企业在构建应用程序时会遇到一个问题:使用即时通讯源码自己开发好还是对接云服务好?本文将探讨这两种方法的优缺点,并为您提供一些…

环境配置 | Win10 VSCode连接远程服务器里的docker容器

环境:win10, VS code, 远程服务器Ubuntu16.04(远程服务器上已经安装好了dockers), 1.VScode下载 网址:Download Visual Studio Code - Mac, Linux, Windows 下载后双击运行 2.VSCode上配置 STEP 1.点击vscode右边工具栏点击拓展…

《嵌入式系统的WEB开发》

硬件平台:RK1109/T31/RK3308 软件系统:Linux,Lighttpd/ Nginx, FCGI, HTML/JS 选择使用FCGI,除了使用C 开发调用系统资源方便外. FCGI 可以解决CGI 多入口的问题,统一的入口,对数据同步,都是比…

Python 基于 Django 的学生成绩管理系统,可视化界面(附源码,教程)

1简介 对于学生成绩管理系统,充分运用现代化的信息技术手段,对于学生成绩信息管理发展的趋势就是信息化,信息化时代下的信息管理,需要深化信息管理体制与手段的改革,充分运用信息化手段来全方位的进行学生成绩管理系统…

VTK交互功能-callback/observe模式-vtkCommand

前言:本博文为系列博文,尝试将vtk交互功能讲清楚,包括常用的接口分析、常用的交互逻辑以及常用的交互Widget。若各位小伙伴有需要请多多关注,也希望能给各位小伙伴有所帮助。vtkCommand作为callback/obseve交互模式的基类&#xf…

0201自动配置类的导入-自动装配原理-springboot2.7.x系列

1简介 Spring Boot是基于Spring框架的,它的原理也是基于Spring框架的。 Spring框架是一个非常强大的框架,它提供了一系列的模块来帮助开发人员构建企业级的应用程序。Spring框架的核心是控制反转(Inversion of Control,IoC&…

大数据技术之Spark Streaming概述

前言 数据处理延迟的长短 实时数据处理:毫秒级别离线数据处理:小时 or 天 数据处理的方式 流式(streaming)数据处理批量(batch)数据处理 spark Streaming也是基于sparkCore,所以底层的核心没有变…