【JavaEE】锁的策略

作者主页:paper jie_博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将MySQL基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《MySQL》《C语言》《javaSE》《数据结构》等

内容分享:本期将会分享锁的策略知识.这也是面试题常考的问题.

目录

乐观锁与悲观锁

轻量级锁与重量级锁

自旋锁与挂起等待锁

普通互斥锁和读写锁

为什么要引入读写锁

公平锁与非公平锁

可重入锁和不可重入锁

synchronized锁与操作系统自带锁对比

synchronized的优化策略

锁的升级

偏向锁阶段(没有其他锁来竞争)

轻量级锁阶段(有锁竞争,但是不多)

重量级锁阶段(有锁竞争,且很多)

锁消除

锁粗化

相关面试题

你是什么理解乐观锁和悲观锁的,具体怎么实现?

介绍一下读写锁

synchronized是可重入锁吗

什么是自旋锁,为什么要使用自旋锁呢,缺点是什么?


乐观锁与悲观锁

乐观锁就是在加锁前,预估发生锁冲突的概率不大,在进行加锁的时候做的工作不多.这样加锁的速度就会比较快,但是会更容易消耗CPU资源.

悲观锁就是在加锁前,预估发生锁冲突的概率比较大.在进行加锁的时候做的工作就比较多.这样的加锁速度就会比较慢,但这个过程发生问题的几率就不大,还可以更节省CPU资源.

举个栗子:

这就好比两个同学去问问题.A同学认为老师有时间就直接跑过去了,而B同学会先给老师打个电话确认一下老师有没有空再去.A同学就是乐观锁,B同学就是悲观锁. 显而易见,A同学就容易遇到老师很忙的情况,而B同学就可以避免.但老师要是不忙,A同学的速度就会更快.

轻量级锁与重量级锁

轻量级锁就是加锁的开销小,加锁速度快.因为轻量级锁不怎么到涉及线程的调度,所以开销就比较小.

而重量级锁就是加锁的开销比较大,加锁速度慢.因为重量级锁涉及到大量的线程调度,遇到锁竞争就需要将线程调度出CPU,等待解锁再由系统随机唤醒一个等待线程,所所以开销会比较慢.

这里可以理解为轻量级锁就是乐观锁,重量级锁就是悲观锁.而前者是站在结果发生后的角度来评价,后者是站在发生前的角度来预估.

自旋锁与挂起等待锁

自旋锁是轻量级锁的经典实现,自旋锁也就是乐观锁. 它就是在加锁的时候搭配一个while循环,加锁成功就退出循环,要是没有成功发生堵塞,也不会退出CPU,而是通过while循环不断尝试获取到锁. 这种循环就叫做自旋.一旦当其他线程释放锁后,它就可以第一时间拿到锁了. 但是自旋锁只能在锁竞争不大的情况下使用,不然就会白白自旋,浪费CPU资源.

挂起等待锁是重量级锁的经典实现.挂起等待锁也就是悲观锁. 它就是在尝试加锁失败后不会再次尝试获取锁.而是调度出CPU,等待其他线程释放锁后,由系统来随机唤醒一个等待的线程.因为它在等待的过程中会有内核调度器介入,会有大量的线程调度,获取锁的时间就会比较慢.挂起等待锁的适用场景是在锁冲突比较严重的情况下.

而在我们Java中,Synchronized这个锁是一个乐观锁/悲观锁自适应,轻量级锁/重量级锁自适应,自旋/挂起等待锁自适应.它可以根据不同的场景来做切换.

普通互斥锁和读写锁

我们Java的synchronized就是普通互斥锁,不管是读还是写都是进行加锁.而读写锁会有两种情况: 1.加读锁 2. 加写锁. 读写锁就是一个线程读时,另一个线程只能读不能写. 一个线程写时,另一个线程不能读也不能写. 

1 读锁和读锁之间不会发生堵塞.

2 读锁和写锁之间会发生堵塞.

3 写锁和写锁之间会发生堵塞. 

为什么要引入读写锁

因为多线线程进行读操作,它本身就是线程安全的,但是对于普通互斥锁来说,两个线程读,也会发生锁竞争,堵塞,这就会造成一定的性能损失. 但是对读操作不加锁的话,就怕一个线程读,一个线程写,导致读到的内容不完整.

基于这个情况下就引入了读写锁,它就可以解决对于读操作不加锁的问题.直接将读引发的锁竞争的开销给节省下来了,让性能会有很大的提高. 我们在实际开发中读操作是非常多的,使用读写锁对性能就有明显的提升了.

公平锁与非公平锁

公平锁就是遵循先来后到. 假设A锁先进行锁等待,B锁后进行锁等待.等到其他线程将锁释放后,就是A先获取到锁.

非公平锁就是不管你是先进行等待还是后进行等待.等到其他线程释放锁后.大家都可以竞争这把锁.系统的调度是随机的,下一个唤醒谁都不确定.

站在操作系统的角度上来看待锁,就是不公平锁.因为操作系统调度线程是随机的.下一个将谁调度进来,唤醒谁也不确定.我们Java中的synchronized也是不公平锁.而公平锁就需要引入额外的数据结构.

可重入锁和不可重入锁

可重入锁就是对于一个锁对象来说, 同一个线程可以多次获取到这把锁,不会发生死锁.

不可重入锁就是对于一个锁对象来锁,同一个线程不可以多次获取到这个把锁,不然会发生死锁.

我们的synchronized就是可重入锁,它的内部有两个重要的属性, 一个是用来记录当前持有锁的线程.一个是计数器,一次加锁就+1,一次解锁就-1.当变成0就算彻底解锁成功.

synchronized锁与操作系统自带锁对比

synchronized是:

乐观锁/悲观锁自适应

轻量/重量锁自适应

自旋/挂起等待锁自适应

不公平锁

普通互斥锁

系统自带锁是:

悲观锁

重量级锁

挂起等待锁

不公平锁

普通互斥锁

synchronized的优化策略

锁的升级

synchronized内部会有一个升级阶段.当一个线程执行到synchronized这里时,所对象还没被加锁的时候,就会经历: 1.偏向锁阶段 -> 2.轻量级锁阶段 -> 3.重量级锁阶段

偏向锁阶段(没有其他锁来竞争)

就是在这个需要加锁的线程上做一个轻量的标记,并不会真正的去进行加锁.只有等到其他的线程也要对这个锁进行加锁时.它才会在其他线程获取到锁之前将锁获取过来.这样就从偏向锁升级到了轻量级锁.

这里的核心思想就是懒汉模式.不能加锁就不加锁,迫不得已的情况下再进行加锁.这样就可以在没有其他线程需要加锁的情况下省去加锁的开销.

轻量级锁阶段(有锁竞争,但是不多)

这里的轻量级锁实现的就是自旋锁,这里的优点就是可以很快获取到锁,但是会更消耗CPU资源. synchronized内部会记录当前有多少个线程在竞争这把锁,当超过一个量后,轻量级锁就会升级到重量级锁.

重量级锁阶段(有锁竞争,且很多)

这里的重量级锁实现的就是挂起等待锁,遇到锁冲突后就直接挂起等待,也就是调度出CPU,当释放锁后,让系统随机唤醒一个来获取锁.

锁消除

这里也是编译器优化的一种方式.当发现这个代码不需要加锁时,就会将锁消除掉.

锁粗化

锁粗化就是将多个细粒度的锁合并成一个粗粒度的锁. 这里我们可以理解完synchronized{}里的代码越少粒度就越细.大多数情况下是希望粒度越细越容易并发执行代码,但有的情况下还是希望粒度粗一点.

比如:这里的加锁操作太多就会导致锁竞争的开销过大,每次加锁可能都会堵塞.这时将细粒度的锁变成粗粒度的锁就节省了锁堵塞的开销,也会提高效率.

相关面试题

你是什么理解乐观锁和悲观锁的,具体怎么实现?

乐观锁就是在加锁前认为发生锁冲突的几率不大,在加锁的时候做的工作就不多,就不会真的加锁,而是直接尝试访问数据.在访问数据的同时便辨别当前数据是不是出现访问异常.

悲观锁就是在加锁前认为发生锁冲突的概率比较大,在进行加锁的时候做的工作就比较多,每次访问变量前都会去真正的进行加锁.

悲观锁的实现就是先加锁,获取到锁再来操作数据,获取不到锁就等待.乐观锁的实现可以引入一个版本号,借助版本号识别出当前的数据访问是不是有冲突.

介绍一下读写锁

读写锁就是把读操作和写操作分别进行加锁. 读锁与读锁之间不会发生堵塞. 读锁与写锁之间会发生堵塞.写锁和写锁之间也会发生堵塞. 这就是一个线程读的时候,另一个线程只能读不能写.而一个线程写的时候,另一个线程不能读也不能写. 一般使用读写锁的都是在读非常频繁,但写不频繁的场景下.

synchronized是可重入锁吗

synchronized是可重入锁.可重入锁是指在一个线程内,可以对同一个锁进行多次加锁,不会产生死锁.而synchronized可以重入是因为它内部有两个重要的属性,一个是记录持有这个锁的线程身份,另一个是计数器(记录加锁的次数). 如果发现当前加锁的线程就是持有锁的线程,计数器就会++.

什么是自旋锁,为什么要使用自旋锁呢,缺点是什么?

自旋锁就是当一个线程发生锁竞争后,它不会挂起等待,而是通过while来不断循环来尝试再次获取到锁.一但其他线程释放锁后它就可以立刻获取到锁. 

因为使用自旋锁在第一次获取失败后第二次获取尝试会很快来到,这样就可以快速获取到锁.

优点就是获取锁的速度快,更高效,在锁持有时间比较短且锁竞争小的场景下非常有用,却点就是如果锁竞争多且锁持有时间长的场景下就没有用了且非常浪费CPU资源,因为它一直在不断循环,做的都是无用功.


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

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

相关文章

20.Java程序设计-基于SSM框架的安卓掌上校园生活系统的设计与实现

摘要: 随着移动互联网技术的快速发展,校园生活信息化成为提高学校管理效率、方便学生生活的关键。本研究以基于SSM(Spring Spring MVC MyBatis)框架的技术体系为基础,致力于设计与实现一款功能强大、高效稳定的安卓…

现代雷达车载应用——第2章 汽车雷达系统原理 2.6节 雷达设计考虑

经典著作,值得一读,英文原版下载链接【免费】ModernRadarforAutomotiveApplications资源-CSDN文库。 2.6 雷达设计考虑 上述部分给出了汽车雷达基本原理的简要概述。在雷达系统的设计中,有几个方面是必不可少的,它们决定了雷达系…

VUE学习三、前端项目部署

1.前端项目打包 执行命令 npm run build:prod正常命令结束 , 会在前端项目里面出现dist文件夹 2.nginx下载安装 nginx下载 : http://nginx.org/en/download.html Windows 下载版本 Mainline version:Mainline 是 Nginx 目前主力在做的版本,可以说…

《使用ThinkPHP6开发项目》 - ThinkPHP6使用使用中间件验证登录Token

https://blog.csdn.net/centaury32/article/details/134997438 按照https://blog.csdn.net/centaury32/article/details/134999029的方法验证登录Token,那么每一步都需要写同样一段代码,这里可以结合中间件进行验证 一、创建中间件:php thi…

QT----第三天,Visio stdio自定义封装控件,鼠标事件,定时器,事件分发器过滤器,绘图事件

目录 第三天1 自定义控件封装2 QT鼠标事件3 定时器4 event事件分发器5 事件过滤器6 绘图事件Qpainter 源码:CPP学习代码 第三天 1 自定义控件封装 新建一个QT widgetclass,同时生成ui,h,cpp文件 在smallWidget.ui里添加上你想要的控件并调试大小 回到…

ISSUE的基本概念

ISSUE:将符合一定条件的指令从发射队列(IssueQueue)中选出来,并送到FU中执行的过程; ISSUE QUEUE也称之为reservation station, 其按照一定的规则,选择那些源操作数都已经准备好的指令,将其送到FU中执行,这个过程称为…

11.jvm第三方工具使用实践

目录 概述GCEasy官网jvm内存占用情况关键性能指标堆内存与元空间优化 MAT安装MAT相关概念说明内存泄漏与内存溢出shallow heap及retained heapoutgoing references与incoming referencesDominator Tree GCViewerArthas下载安装与启动jdk8jdk 11jdk11自定义boot jarjdk17 常用命…

LVS负载均衡集群和NAT模式部署

目录 一、群集的类型及含义 二、LVS的三种工作模式 一、根据群集所针对的目标差异,可分为三种类型 二、LVS的负载调度算法 三、ipvsadm 工具选项 四、NAT模式 LVS负载均衡群集部署 1.共享服务器配置: 2.节点服务器1配置 3.节点服务器2 4.配置负…

spring 笔记八 SpringMVC异常处理和SpringMVC拦截器

文章目录 SpringMVC拦截器拦截器(interceptor)的作用拦截器和过滤器区别拦截器是快速入门拦截器方法说明 SpringMVC拦截器拦截器(interceptor)的作用拦截器和过滤器区别拦截器是快速入门拦截器方法说明 SpringMVC异常处理异常处理…

网络服务IP属地发生变化的原因有哪些?

近期,许多用户发现自己的网络服务IP属地发生了变化。原本固定的IP地址不再是静态的,而是发生了变动。这一现象引起了广大用户的关注和疑惑,对网络服务的使用和信息安全产生了影响。为了解决用户的疑虑,我们对此现象进行了深入探究…

java.lang.UnsupportedOperationException

一、背景 记录一次小坑… 最近在写一个关于Excel导出的小需求,由于系统都有一些工具类,还有原来已经做好的导出,直接拿过来改了改就用了,没想到直接报错,尴尬。 还是那句话,别人都能用,我复制…

国产浪潮服务器:风扇免手动调节脚本

简介:浪潮集团,是中国本土顶尖的大型IT企业之一,中国领先的云计算、大数据服务商。浪潮集团旗下拥有浪潮信息、浪潮软件、浪潮国际,业务涵盖云计算、大数据、工业互联网等新一代信息技术产业领域,为全球120多个国家和地…

【人工智能】模糊推理

模糊推理:交叉的隶属函数,方法如模糊规则中推理方法一致,step3中的每一个格子都有所应的隶属关系函数 引言:“生活中的模糊关系”模糊集合模糊集合的定义模糊集合的表示法模糊集合表示法示例 隶属函数 模糊规则模糊计算的流程 引言&#xff1…

RT-DETR 目标过线计数

使用 Ultralytics RT-DETR 进行目标计数 🚀 实际应用场景 物流水产养殖使用 Ultralytics RT-DETR 进行传送带包裹计数使用 Ultralytics RT-DETR 在海中进行鱼类计数请使用最新代码(2023年12月8日后),旧版本不支持! 示例 “目标计数示例” 目标计数 from ultralytics

初识Redis缓存,一文掌握Redis重要知识文集。

🏆作者简介,普修罗双战士,一直追求不断学习和成长,在技术的道路上持续探索和实践。 🏆多年互联网行业从业经验,历任核心研发工程师,项目技术负责人。 🎉欢迎 👍点赞✍评论…

存储拆分后,如何解决唯一主键问题?

之前我们讲到了分库分表,现在考虑这样一个问题:在单库单表时,业务 ID 可以依赖数据库的自增主键实现,现在我们把存储拆分到了多处,如果还是用数据库的自增主键,势必会导致主键重复。 那么我们应该如何解决…

【算法】【动规】乘积为正数的最长子数组长度

跳转汇总链接 👉🔗算法题汇总链接 1.1 乘积为正数的最长子数组长度 🔗题目链接 给你一个整数数组 nums ,请你求出乘积为正数的最长子数组的长度。 一个数组的子数组是由原数组中零个或者更多个连续数字组成的数组。 请你返回乘积…

docker镜像与容器的迁移

docker容器迁移有两组命令,分别是 save & load :操作的是images, 所以要先把容器commit成镜像export & import:直接操作容器 我们先主要看看他们的区别: 一 把容器打包为镜像再迁移到其他服务器 如把mysq…

RMQ算法总结

知识概览 RMQ又叫ST表、跳表,可以用来解决区间最值问题,这里这有查询没有修改。当然,这样的问题用线段树也是可以解决的。RMQ算法本质上是倍增动态规划,它的思想是先倍增预处理再查询。f(i, j)表示从i开始,长度是的区…

【Jeecg Boot 3 - 第二天】2.1、nginx 部署 JEECGBOOT VUE3

一、场景 二、实战 ▶ 2.1、打包(build 前端) > Stage 1:修改配置文件 .env.production(作用:指向后端接口地址) > Stage 2:点击build(作用&#xff1…