各种锁的概述

乐观锁与悲观锁

悲观锁指对数据被外界修改持保守态度,认为数据很容易就会被其他线程修改,所以在数据被处理前先对数据进行加锁,并在整个数据处理过程中,使数据处于锁定状态。

悲观锁的实现往往依靠数据库提供的锁机制,即在数据库中,在对数据记录操作前给记录加排它锁。

如果获取锁失败,则说明数据正在被其他线程修改,当前线程则等待或者抛出异常。

如果获取锁成功,则对记录进行操作,然后提交事务后释放排它锁。
在这里插入图片描述
假设updateEntry、query、update方法都使用了事务切面的方法,并且事务传播性被设置为required。

执行updateEntry方法时如果上层调用方法里面没有开启事务,则会即时开启一个事务,然后执行代码(1)。

代码(1)调用了query方法,其根据指定id从数据库里面查询出一个记录。

由于事务传播性为requried,所以执行query时没有开启新的事务,而是加入了updateEntry开启的事务,也就是在updateEntry方法执行完毕提交事务时,query方法才会被提交,就是说记录的锁定会持续到updateEntry执行结束。

代码(2)则对获取的记录进行修改,代码(3)把修改的内容写回数据库,同样代码(3)的update方法也没有开启新的事务,而是加入了updateEntry的事务。

也就是updateEntry、query、update方法共用同一个事务。

当多个线程同时调用updateEntry方法,并且传递的是同一个id时,只有一个线程执行代码(1)会成功,其他线程则会被阻塞,这是因为在同一时间只有一个线程可以获取对应记录的锁,在获取锁的线程释放锁前(updateEntry执行完毕,提交事务前),其他线程必须等待,也就是在同一时间只有一个线程可以对该记录进行修改。

乐观锁是相对悲观锁来说的,它认为数据在一般情况下不会造成冲突,所以在访问记录前不会加排它锁,而是在进行数据提交更新时,才会正式对数据冲突与否进行检测。

具体来说,根据update返回的行数让用户决定如何去做。将上面的例子改为使用乐观锁的代码如下。
在这里插入图片描述
当多个线程调用updateEntry方法并且传递相同的id时,多个线程可以同时执行代码(1)获取id对应的记录并把记录放入线程本地栈里面,然后可以同时执行代码(2)对自己栈上的记录进行修改,多个线程修改后各自的entry里面的属性应该都不一样了。

然后多个线程可以同时执行代码(3),代码(3)中的update语句的where条件里面加入了version=#{version}条件,并且set语句中多了version=${version}+1表达式,该表达式的意思是,如果数据库里面id=#{id} and version=#{version}的记录存在,则更新version的值为原来的值加1,这有点CAS操作的意思。

假设多个线程同时执行updateEntry并传递相同的id,那么它们执行代码(1)时获取的Entry是同一个,获取的Entry里面的version值都是相同的(这里假设version=0)。

当多个线程执行代码(3)时,由于update语句本身是原子性的,假如线程A执行update成功了,那么这时候id对应的记录的version值由原始version值变为了1。其他线程执行代码(3)更新时发现数据库里面已经没有了version=0的语句,所以会返回影响行号0。

在业务上根据返回值为0就可以知道当前更新没有成功,那么接下来有两个做法,如果业务发现更新失败了,下面可以什么都不做,也可以选择重试,如果选择重试,则updateEntry的代码可以修改为如下。

在这里插入图片描述
如上代码使用retryNum设置更新失败后的重试次数,如果代码(3.1)执行后返回0,则说明代码(1.1)获取的记录已经被修改了,则循环一次,重新通过代码(1.1)获取最新的数据,然后再次执行代码(3.1)尝试更新。

这类似CAS的自旋操作,只是这里没有使用死循环,而是指定了尝试次数。

乐观锁并不会使用数据库提供的锁机制,一般在表中添加version字段或者使用业务状态来实现。乐观锁直到提交时才锁定,所以不会产生任何死锁。

公平锁与非公平锁

根据线程获取锁的抢占机制,锁可以分为公平锁和非公平锁,公平锁表示线程获取锁的顺序是按照线程请求锁的时间早晚来决定的,也就是最早请求锁的线程将最早获取到锁。而非公平锁则在运行时闯入,也就是先来不一定先得。
ReentrantLock提供了公平和非公平锁的实现。

  • 公平锁:ReentrantLock pairLock =new ReentrantLock(true)。
  • 非公平锁:ReentrantLock pairLock =new ReentrantLock(false)。如果构造函数不传递参数,则默认是非公平锁。

例如,假设线程A已经持有了锁,这时候线程B请求该锁其将会被挂起。当线程A释放锁后,假如当前有线程C也需要获取该锁,如果采用非公平锁方式,则根据线程调度策略,线程B和线程C两者之一可能获取锁,这时候不需要任何其他干涉,而如果使用公平锁则需要把C挂起,让B获取当前锁。
在没有公平性需求的前提下尽量使用非公平锁,因为公平锁会带来性能开销。

独占锁与共享锁

根据锁只能被单个线程持有还是能被多个线程共同持有,锁可以分为独占锁和共享锁。

独占锁保证任何时候都只有一个线程能得到锁,ReentrantLock就是以独占方式实现的。

共享锁则可以同时由多个线程持有,例如ReadWriteLock读写锁,它允许一个资源可以被多线程同时进行读操作。

独占锁是一种悲观锁,由于每次访问资源都先加上互斥锁,这限制了并发性,因为读操作并不会影响数据的一致性,而独占锁只允许在同一时间由一个线程读取数据,其他线程必须等待当前线程释放锁才能进行读取。

共享锁则是一种乐观锁,它放宽了加锁的条件,允许多个线程同时进行读操作。

什么是可重入锁

当一个线程要获取一个被其他线程持有的独占锁时,该线程会被阻塞,那么当一个线程再次获取它自己已经获取的锁时是否会被阻塞呢?如果不被阻塞,那么我们说该锁是可重入的,也就是只要该线程获取了该锁,那么可以无限次数地进入被该锁锁住的代码。

在这里插入图片描述
调用helloB方法前会先获取内置锁,然后打印输出。

之后调用helloA方法,在调用前会先去获取内置锁,如果内置锁不是可重入的,那么调用线程将会一直被阻塞。

实际上,synchronized内部锁是可重入锁。可重入锁的原理是在锁内部维护一个线程标示,用来标示该锁目前被哪个线程占用,然后关联一个计数器。

一开始计数器值为0,说明该锁没有被任何线程占用。当一个线程获取了该锁时,计数器的值会变成1,这时其他线程再来获取该锁时会发现锁的所有者不是自己而被阻塞挂起。

但是当获取了该锁的线程再次获取锁时发现锁拥有者是自己,就会把计数器值加+1,当释放锁后计数器值-1。

当计数器值为0时,锁里面的线程标示被重置为null,这时候被阻塞的线程会被唤醒来竞争获取该锁。

自旋锁

由于Java中的线程是与操作系统中的线程一一对应的,所以当一个线程在获取锁(比如独占锁)失败后,会被切换到内核状态而被挂起。

当该线程获取到锁时又需要将其切换到内核状态而唤醒该线程。

而从用户状态切换到内核状态的开销是比较大的,在一定程度上会影响并发性能。

自旋锁则是,当前线程在获取锁时,如果发现锁已经被其他线程占有,它不马上阻塞自己,在不放弃CPU使用权的情况下,多次尝试获取(默认次数是10,可以使用-XX:PreBlockSpinsh参数设置该值),很有可能在后面几次尝试中其他线程已经释放了锁。

如果尝试指定的次数后仍没有获取到锁则当前线程才会被阻塞挂起。

由此看来自旋锁是使用CPU时间换取线程阻塞与调度的开销,但是很有可能这些CPU时间白白浪费了。

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

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

相关文章

springboot 注解+AOP实现接口方法出入参打印

注解AOP实现接口方法出入参打印 0、前言1、自定义注解2、定义AOP3、封装日志打印方法 0、前言 针对加入spring bean容器的实例方法,通过注解aop实现打印方法调用的出入参,通过注解可以配置是否打印出入参,出入参中的单列集合或双列集合是否打…

计算机体系结构期末复习流程大纲

1.存储器和cache 存储器的容量、速度与价格之间的要求是相互矛盾的,速度越快,没bit位价格越高,容量越大,速度越慢,目前主存一般有DRAM构成。 处理器CPU访问存储器的指标: 延迟时间(Latency&am…

【C++】—— 工厂模式详解

目录 (一)工厂模式的特点 (二)工厂模式分类 1、简单工厂模式 2、工厂方法模式 3、抽象工厂模式 (三)总结与回顾 (一)工厂模式的特点 1、优势 ⼯⼚模式是⼀种创建型设计模式&a…

快速入门Visual Studio 2022开发.Net Framework研发环境指南

IDE工具 Visual Studio 2022 Vs2022企业版 - VisualStudioSetup.exe Visual Studio Code VSCodeUserSetup-x64-1.66.2.exeVSCodeUserSetup-x64-1.67.0-insider.exe IDE环境 编程字体YaHei.Consolas YaHei.Consolas.1.12.ttf IDE插件 Visual Studio Code常用插件 Chinese…

STM32单片机使用STM32CubeMX和Simulink进行联合开发-1

STM32单片机使用STM32CubeMX和Simulink进行联合开发-1 使用Simulink中的STM32扩展功能包配合STM32CubeMX进行联合开发,包含环境配置和实际应用教程。 目录 STM32单片机使用STM32CubeMX和Simulink进行联合开发-1一、环境配置1.安装扩展功能包2.安装STM32-MAT包二、创建STM32Cu…

django项目基础后端功能使用

参考材料 Django新手项目实例-CSDN博客 一、django安装 pip3 install django 二、django项目新建 在目标目下执行 django-admin startproject testdjgo 执行完成后生成对应项目路径 三、django路由功能编写 /xxx/urls.py中编写路由信息,并且把路由转发到对应…

说出来你别不信,盲订问界M9的原因 你们想错了,他们只图这个

文|AUTO芯球 作者|李瑞 怎么还有人说华为是骗子? 华为一张海报说问界M9上市6天,大定超过3万台。有些人就说这是假的,反正没第三方数据,华为可以随便写。 我去,我作为一名大定问界M9的车主,就奉劝哪些黑子…

Linux篇之Centos中将系统时间设置为本地时间

要在 CentOS 上将系统时间设置为本地时间,可以按照以下步骤进行操作: 1.首先,你需要确定你想要设置的本地时间。例如,如果你想要将系统时间设置为当前时间(假设是北京时间),则可以使用以下命令获…

5.vue学习笔记(数组变化的侦测+计算属性+Class绑定)

文章目录 1.数组变化的侦测1.1.变更方法1.2.替换一个数组 2.计算属性计算属性缓存vs方法 3.Class绑定3.1.绑定对象3.2.多个对象的绑定形式3.3.绑定数组3.4.数组与对象 1.数组变化的侦测 1.1.变更方法 vue能够侦听响应式数组的变更方法,并在它们被调用时出发相关的…

Taro+vue3 实现电影切换列表

1.需求 我们在做类似于猫眼电影的小程序或者H5 的时候 我们会做到那种 左右滑动的电影列表,这种列表一般带有电影场次 2.效果 3.说明 这种效果在淘票票 猫眼电影上 都有的 ,一般电影类型的H5 或者小程序 这个是都有的 第一是好看 第二是客观性比较好 …

【Linux】【开发】Linux module名和C语言文件名相同导致的编译问题

🐚作者简介:花神庙码农(专注于Linux、WLAN、TCP/IP、Python等技术方向)🐳博客主页:花神庙码农 ,地址:https://blog.csdn.net/qxhgd🌐系列专栏:Linux技术&…

Mysql InnoDB行锁深入理解

Record Lock记录锁 Record Lock 称为记录锁,锁住的是一条记录。而且记录锁是有 S 锁和 X 锁之分的: 当一个事务对一条记录加了 S 型记录锁后,其他事务也可以继续对该记录加 S 型记录锁(S 型与 S 锁兼容),…

物联网与智能家居:跨境电商与未来生活的融合

物联网(Internet of Things,IoT)和智能家居技术正迅速崛起,成为跨境电商领域的创新引擎。这两者的巧妙结合不仅为消费者提供更智能、便捷的生活方式,同时也为电商平台和制造商带来了全新的商机。本文将深入探讨物联网与…

Flink之Task重启策略

Task重启策略 1 策略API noRestart 无参数,task失败后不重启,整个job同时失败,默认策略. 代码示例 RestartStrategies.noRestart();fixedDelayRestart 参数注释restartAttempts最大重启次数delayBetweenAttempts重启时间间隔代码示例 // 最多重启5次,每次任务失败后间隔1s重启…

记录实现级联选择器多选功能时主要用到的函数

级联选择器多选功能时主要用到的函数 1、校验所给层级是否有效并且是否为完整路径 // 功能:涉及到级联选择器回显时,需校验是否为完整的路径,是则回显,不是则无无效路径 // options为级联数据,selectedPaths为要校验…

C++模板——(3)类模板

归纳编程学习的感悟, 记录奋斗路上的点滴, 希望能帮到一样刻苦的你! 如有不足欢迎指正! 共同学习交流! 🌎欢迎各位→点赞 👍 收藏⭐ 留言​📝 勤奋,机会,乐观…

使用pyinstaller打包生成exe(解决gradio程序的打包问题)

解决 [Errno 2] No such file or directory: gradio_client\types.json 问题,不需要手动创建hook文件 解决 FileNotFoundError: [Errno 2] No such file or directory: gradio\blocks_events.pyc 问题,不需要将pyi文件重命名为pyc文件 最终实现gradio程…

卡码网Java基础课 | 7. 摆平积木,8. 奇怪的信

卡码网Java基础课|7. 摆平积木 7. 摆平积木8. 奇怪的信 7. 摆平积木 import java.util.Scanner; import java.util.ArrayList;public class Main{public static void main(String[] args){Scanner sc new Scanner(System.in);while(sc.hasNextInt()){int n sc.nextInt();if(…

【CSS】讲一讲BFC、IFC、GFC、FFC

1. 前言 FC(Formatting Contexts),是CSS2.1的一个概念,是页面中的一块渲染区域,具有一套渲染规则,决定FC中子元素如何定位,以及和其他元素的关系和相互作用。在说FC之前说一下文档流。 1.1. 普…