【Java多线程学习5】什么是悲观锁,什么是乐观锁?如何实现乐观锁、乐观锁存在哪些问题
【Java多线程学习5】什么是悲观锁,什么是乐观锁?如何实现乐观锁、乐观锁存在哪些问题
一、什么是悲观锁
概述
悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作的时候都会上锁,这样其他线程想拿到这个资源就会阻塞直到锁被上一个持有者释放。也就是说,共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程。
像Java中synchronized和ReentrantLock等独占锁就是悲观锁的思想。
- synchronized示例如下:
private Lock lock = new ReentrantLock();public void deal() {//上锁lock.lock();try {//需要同步的操作} finally {//释放锁lock.unlock();}}
- ReentrantLock示例如下:
private Lock lock = new ReentrantLock();public void deal() {//上锁lock.lock();try {//需要同步的操作} finally {//释放锁lock.unlock();}}
高并发的场景下,激烈的锁竞争会造成线程阻塞,大量阻塞线程会导致系统的上下文切换,增加系统的性能开销。并且,若未及时释放锁,悲观锁还可能会存在死锁问题,影响代码的正常运行。
二、什么是乐观锁
概述
乐观锁总是假设最好的情况,认为共享资源每次被访问的时候不会出现问题,线程可以不停地执行,无需加锁也无需等待,只是在提交修改的时候去验证对应的资源(也就是数据)是否被其它线程修改了
乐观锁的实现方式:版本号机制 或 CAS算法
总体来说:
- 悲观锁通常多用于写比较多的情况下(多写场景,竞争激烈),这样可以避免频繁失败和重试影响性能,悲观锁的开销是固定的。不过,如果乐观锁解决了频繁失败和重试这个问题的话(比如LongAdder),也是可以考虑使用乐观锁的,要视实际情况而定。
- 乐观锁通常多于写比较少的情况下(多读场景,竞争较少),这样可以避免频繁加锁影响性能。不过,乐观锁主要针对的对象是单个共享变量(参考java.util.concurrent.atomic包下面的原子变量类)。
乐观锁的实现方式
乐观锁一般会使用版本号机制 或 CAS算法。CAS算法相对多一些。
方式一:版本号机制
一般是在数据表中加上一个数据版本号 version 字段,表示数据被修改的次数。当数据被修改时,version 值会加一。当线程 A 要更新数据值时,在读取数据的同时也会读取 version 值,在提交更新时,若刚才读取到的 version 值为当前数据库中的 version 值相等时才更新,否则重试更新操作,直到更新成功。
举一个例子:
有一个账户金额表 ,当前账户金额100,表中有一个version字段当前值为1。
线程a,向账户中存100,线程a读取version为1,此时线程b也读取了version的数据为1,并要取20。
线程a,将version=1和金额200一同提交到数据库更新,由于线程a的提交版本等于当前数据库的版本,更新成功。
线程b,也将version=1,金额80提交到数据库更新,由于线程b的提交版本1不等于当前数据库的版本2,操作b的提交被驳回。
方式二:CAS算法
CAS 的全称是 Compare And Swap(比较与交换)算法 ,用于实现乐观锁的一种技术。
CAS算法的思想简单,核心思路是:比较内存中的当前变量的值与预期值是否相等,如果相等则将其值改为新值,如果不相等则当前线程放弃更新,CAS操作失败。
CAS操作设计的三个操作数:
- V:要更新的变量值(Var)
- E:预期值(Expected)
- N:拟写入的新值(New)
当且仅当 V 的值等于 E 时,CAS 通过原子方式用新值 N 来更新 V 的值。如果不等,说明已经有其它线程更新了 V,则当前线程放弃更新。
举一个简单的例子:
线程 a 要修改变量 i 的值为 10,i 原值为 1(V = 1,E=1,N=10,假设不存在 ABA 问题)。
i 与 1 进行比较,如果相等, 则说明没被其他线程修改,可以被设置为 6 。
i 与 1 进行比较,如果不相等,则说明被其他线程修改,当前线程放弃更新,CAS 操作失败。
当多个线程同时使用 CAS 操作一个变量时,只有一个会胜出,并成功更新,其余均会失败,但失败的线程并不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。
乐观锁存在的问题
1、ABA 问题
ABA 问题是乐观锁最常见的问题。
ABA 问题的解决思路是在变量前面追加上版本号或者时间戳。
JDK 1.5 以后的 AtomicStampedReference 类就是用来解决 ABA 问题的,其中的 compareAndSet() 方法就是首先检查当前引用是否等于预期引用,并且当前标志是否等于预期标志,如果全部相等,则以原子方式将该引用和该标志的值设置为给定的更新值
2、只能保证一个共享变量的原子操作
CAS 只对单个共享变量有效,当操作涉及跨多个共享变量时 CAS 无效 ,也无法实现对代码块或是方法进行原子操作。
3、循环时间开销大
CAS 经常会用到自旋操作来进行重试,也就是不成功就一直循环执行直到成功。如果长时间不成功,会给 CPU 带来非常大的执行开销。
相关文章:
【Java多线程学习5】什么是悲观锁,什么是乐观锁?如何实现乐观锁、乐观锁存在哪些问题
【Java多线程学习5】什么是悲观锁,什么是乐观锁?如何实现乐观锁、乐观锁存在哪些问题 一、什么是悲观锁 概述 悲观锁总是假设最坏的情况,认为共享资源每次被访问的时候就会出现问题(比如共享数据被修改),所以每次在获取资源操作…...
OSPF协议RIP协议+OSPF实验(eNSP)
本篇博客主要讲解单区域的ospf,多区域的仅作了解。 目录 一、OSPF路由协议概述 1.内部网关协议和外部网关协议 二、OSPF的应用环境 1.从以下几方面考虑OSPF的使用 2.OSPF的特点 三、OSPF重要基本概念 3.1,辨析邻居和邻接关系以及七种邻居状态 3…...
leetcode每日一练-第108题-将有序数组转换为二叉搜索树
一、思路 递归 二、解题方法 在给定中序遍历序列数组的情况下,每一个子树中的数字在数组中一定是连续的,因此可以通过数组下标范围确定子树包含的数字,下标范围记为 [left,right]。对于整个中序遍历序列,下标范围从 left0到 ri…...
王道《操作系统》学习(二)—— 进程管理(二)
2.1 处理机调度的概念、层次 2.1.1 调度的基本概念 2.1.2 调度的三个层次 (1)高级调度(作业调度) (2)中级调度(内存调度) 补充知识:进程的挂起状态和七状态模型 &#x…...
Vulnhub: shenron: 3靶机
kali:192.168.111.111 靶机:192.168.111.171 信息收集 端口扫描 nmap -A -sC -v -sV -T5 -p- --scripthttp-enum 192.168.111.171 修改hosts后访问目标80端口,发现是wordpress wpscan收集目标用户,爆破出密码:ilov…...
Kubernetes高可用集群二进制部署(二)ETCD集群部署
Kubernetes概述 使用kubeadm快速部署一个k8s集群 Kubernetes高可用集群二进制部署(一)主机准备和负载均衡器安装 Kubernetes高可用集群二进制部署(二)ETCD集群部署 Kubernetes高可用集群二进制部署(三)部署…...
mysql主从复制及原理
目录 主从复制原理实现主从复制 主从复制原理 主要基于MySQL二进制日志 主要包括三个线程(2个I/O线程,1个SQL线程) 1、MySQL将数据变化记录到二进制日志中; 2、Slave将MySQL的二进制日志拷贝到Slave的中继日志中; …...
MQTT服务器详细介绍:连接物联网的通信枢纽
随着物联网技术的不断发展,MQTT(Message Queuing Telemetry Transport)协议作为一种轻量级、可靠、灵活的通信协议,被广泛应用于物联网领域。在MQTT系统中,MQTT服务器扮演着重要的角色,作为连接物联网设备和…...
通过VBA宏合并Excel工作表
工作中经常会用到的把几个Excel文件合并到一个,或者是把一个Excel文件里的所有Sheet合并到一个Sheet来进行统计。下面分别提供用vba宏来解决这两个问题的方法。 1、合并Excel文件 打开一个空Excel文件,AltF11,插入一个模块,开始…...
Mac 定时重启 TouchBar 脚本(缓解闪烁问题)
背景 Mac 笔记本 TouchBar 是真的脆啊,合盖使用一段时间就废了,右侧一直闪烁简直亮瞎眼 😂 经过观察,总结出闪烁规律如下: 工作状态:不断操作电脑时,触控栏处于工作状态,几乎不闪…...
Redis主从复制、哨兵机制、集群分片
目录 一.主从复制 1.概述 2.主从架构相比于单点架构的优势 3.主从复制原理和工作流程 第一次同步 第一阶段:建立链接、协商同步 第二阶段:主服务器同步数据给从服务器 第三阶段:主服务器发送新写操作命令给从服务器 基于长连接的命…...
字段填充策略 FieldFill
实体类中有如下属性,通过上面的自动填充属性,我们可以实现在进行插入(insert)操作时对添加了注解TableField(fill FieldFill.INSERT)的字段进行自动填充(解释:后面会写配置自动填充的配置类,该…...
Docker run 启动容器报错
今天在Windows下启动docker容器发现的三个错误: Ports are not available: exposing port TCP 0.0.0.0:1521 -> 0.0.0.0:0: listen tcp 0.0.0.0:1521: bind: Only one usage of each socket address (protocol/network address/port) is normally permitted. 端口…...
Golang之路---03 面向对象——类型断言
类型断言 作用 检查 i 是否为 nil检查 i 存储的值是否为某个类型 使用方式 第一种: t : i.(T)这个表达式可以断言一个接口对象(i)里不是 nil,并且接口对象(i)存储的值的类型是 T,如果断言成…...
Atcoder 做题记录
My OI Blog A R C 155 F \mathbb{ARC \ 155 \ F} ARC 155 F E, F 先咕着,做一些多项式题,这篇题解是我人工翻译的 [1] Double Counting 双重计数 考虑从叶子节点开始,用唯一的方式(如果有的话)来构造出一棵满足条件的树…...
C++之观察者模式(发布-订阅)
目录 模式简介 介绍 优点 缺点 代码实现 场景说明 实现代码 运行结果 模式简介 观察者模式(Observer Pattern),也叫我们熟知的发布-订阅模式。 它是一种行为型模式。 介绍 观察者模式主要关注的是对象的一对多的关系, …...
无头单链表,有完整测试程序
🍟无头单链表 👻无头单链表的所有结点都存储有效信息 👻无头单链表相对带头单链表,在有些涉及更改头节点的函数上需要传二级指针 🍟头文件list.h #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #includ…...
2023年第四届“华数杯”数学建模思路 - 案例:FPTree-频繁模式树算法
## 赛题思路 (赛题出来以后第一时间在CSDN分享) https://blog.csdn.net/dc_sinor?typeblog 算法介绍 FP-Tree算法全称是FrequentPattern Tree算法,就是频繁模式树算法,他与Apriori算法一样也是用来挖掘频繁项集的,…...
MySQL做分布式锁
分布式锁mysql实现方式 方式1:唯一索引 创建锁表,内部存在字段表示资源名及资源描述,同一资源名使用数据库唯一性限制。多个进程同时往数据库锁表中写入对某个资源的占有记录,当某个进程成功写入时则表示其获取锁成功其他进程由于…...
Python学习笔记:变量类型、字符串基本操作
1.注释 单行注释 # 单行注释 多行注释 """ 多行注释 """2.变量类型 # 基本变量类型 a 1 # integer b 1.5 # float c string # String d "string" # string e False # boolean # list\tuple\dictionar…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
(十)学生端搭建
本次旨在将之前的已完成的部分功能进行拼装到学生端,同时完善学生端的构建。本次工作主要包括: 1.学生端整体界面布局 2.模拟考场与部分个人画像流程的串联 3.整体学生端逻辑 一、学生端 在主界面可以选择自己的用户角色 选择学生则进入学生登录界面…...
STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别
OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
招商蛇口 | 执笔CID,启幕低密生活新境
作为中国城市生长的力量,招商蛇口以“美好生活承载者”为使命,深耕全球111座城市,以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子,招商蛇口始终与城市发展同频共振,以建筑诠释对土地与生活的…...
