当前位置: 首页 > news >正文

乐观锁和悲观锁

目录

  • 悲观锁:
  • 乐观锁:
    • CAS算法:
    • 版本号机制:
    • write_condition 机制:
    • 时间戳:
    • ReentrantLock 类:
  • 独占锁:
  • synchronized 关键字:

悲观锁:

1、理解:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿到资源的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
共享资源每次只给一个线程使用,其他的线程阻塞,用完后再把资源转让给其他线程。

2、技术实现:Java中的 synchronized 关键字 和 ReentrantLock 类 等独占锁就是悲观锁思想的实现。

3、应用场景:传统关系型数据库(Mysql,oracle)里面就用到了很多这种锁机制,比如行锁、表锁,读锁(synchronized)、写锁(排他锁)等,都是在做操作之前先上锁。

一般来说,当并发访问和修改的冲突情况比较频繁、数据更新的冲突概率较高时,应该采用悲观锁,以防止读写冲突,保障数据的安全性。(如银行转账等操作)

4、优点:可以保证共享资源的数据一致性和完整性,避免多个线程同时修改共享资源导致的数据混乱和错误。

5、缺点:可能会因为加锁和解锁导致死锁、性能问题、资源争用等问题,锁冲突问题多。

乐观锁:

1>、理解:总是假设最好的情况,每次去拿数据的时候都认为 别人不会去修改,所以不会上锁,但是在更新数据的时候会去判断一下在此期间别人有没有去更新这个数据。

2>、技术实现: 版本号机制 和 CAS 算法 都是乐观锁的实现方式。

3>、应用场景
1、乐观锁适用于写比较少的情况(多读场景),这样可以提高吞吐量
2、NoSQL非关系型数据库提供的类似 write_condition 机制,其实都是提供的乐观锁

当读操作远远超过写操作的情况下,应该优先考虑采用乐观锁,因为悲观锁需要先假定数据发生了冲突,然后再加锁进行保护,这种加锁操作会影响系统的性能,乐观锁适用于数据冲突的概率较小的情况。(如微博访问等)

4>、优点
1、减少锁冲突(通过数据版本控制的方式来保证数据在多个线程中的一致性)
2、性能高(不需要进行加锁、解锁等操作,可提高系统的并发量和吞吐量)
3、方便实现(版本号控制或者时间戳等方式对数据的一致性进行控制)
4、允许并发访问(允许多个线程同时访问同一份数据,只有在数据版本号发生变化时才会发生异常,从而保证了数据在并发访问时的一致性和完整性)

5>、缺点
1、自旋CAS算法导致的ABA问题:
一个变量V初次读取的时候是A值,并且在准备赋值的时候检查到它仍然是A值,但是在这段时间内它的值可能被修改成其他的值,然后又被改回A,那么CAS操作就会误认为它从来没有被修改过,这个问题就被称为CAS操作的ABA问题

2、循环时间长开销大:
自旋CAS(就是不成功就一直循环执行直到成功)如果长时间不成功,会给CPU带来非常大的执行开销。
(优化方法:如果JVM能支持处理器提供的pause指令(cpu指令,让线程在执行的过程中主动暂停一段时间),那么效率就可以提升)

3、只能保证一个共享变量的原子操作:
CAS只对单个共享变量有效,当操作涉及跨多个共享变量时CAS无效。
但是JDK1.5之后有个AtomicReference类,利用这个类我们可以将多个变量放在一个对象里面来进行CAS操作,将多个共享变量合并成一个共享变量来操作

CAS算法:

1、概念:
cas是Java中Unsafe类中的一个方法,即 compare and swap(比较与交换),是一种有名的无锁算法。就是在不使用锁的情况下实现多线程之间的变量同步,也就是在没有线程被阻塞的情况下实现变量的同步,所以也叫非阻塞同步。

cas算法涉及到三个操作数:需要读写的内存值V,进行比较的值A,要写入的新值B,cas会一直自旋的判断V值是否等于A值,相同才会写入新值,不相同就重新获取最新的内存值进行业务逻辑操作后,再进行比较。

示例:

大白话:

1、线程A和线程B都要去获取内存值进行++操作

2、线程A拿到内存值V时V=0,然后就去对V值执行++操作

3、此时线程B也获取到内存值V,获取到的V值也=0,然后也去执行++操作,然后线程B执行速度比较快,执行完业务逻辑++操作后,就再去拿一遍内存值V,把两次获取到的V值进行对比,如果一致,说明线程B在执行++操作的这段时间内,内存值V的值没有被其他线程修改过,所以线程B就可以把自己执行完++操作后的最新值赋值给内存值V,此时内存值V就被线程B从0修改成1了。

4、然后此时的线程A也执行完++操作了,也同样再次去获取内存值V,此时线程A获取到的内存值V已经是被线程B修改过的1了,所以和刚开始获取到的V值(0)进行对比,发现不相等,说明在线程A执行++操作的时候,已经有其他线程把内存值V的值修改了,所以此时的线程A就不能把刚刚执行完++的值赋值给V了。

5、线程A就只能再重新去获取内存值V进行++操作,此时线程A拿到的内存值V = 1,然后再重新++操作,操作完再去获取最新的内存值(如果没被其他线程修改过,则内存值V = 1),再拿来跟执行++操作前获取到的V值(V=1)进行对比,如果对比一致,说明这次在线程A执行++操作的过程中没有其他线程修改过内存值B,所以线程A就可以把自己执行++操作的值赋值给内存值。

6、如果线程A对比发现一开始获取到的内存值和执行完业务逻辑后再去获取的内存值 不一致,那么就继续获取最新的内存值,再继续做业务逻辑操作,操作完再继续拿最新的内存值和一开始的进行对比,这个过程就叫做自旋

在这里插入图片描述

版本号机制:

一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改的时候,version值就会+1。
当线程A要更新数据值的时候,在读取数据的同时也会读取version值,在提交更新的时候会再次读取那条数据的version值,如果两个version值一样的话,才会更新数据,否则就重试刚刚的更新数据操作,直到更新成功。
因为如果没有对比version值,那么线程A在修改数据的期间,有其他线程修改相同的那条数据并提交,那么就会导致数据出错,出现旧数据覆盖新数据等这种情况。
提交版本的version值必须要大于记录当前版本的version值才能执行更新。

write_condition 机制:

可以先理解为是版本号机制的一种体现(版本号机制是为表添加一个版本号的字段,write_condition是根据表中的某个字段的状态来判断数据是否被修改过)

时间戳:

MySQL中的timestamp,或者自己在表中新增一个字段用于存储时间戳,就是在执行数据更新时,除了比较版本号之外,还需要比较时间戳。在执行数据更新时,同时需要更新时间戳的数据,
一般有两种方式:
一种是在数据库层面通过触发器等方式自动更新时间戳字段,
另一种是在应用层面手动更新时间戳字段

ReentrantLock 类:

就是创建一个类,ReentrantLock.lock()获取锁,然后try/catch写代码,ReentrantLock.unlock()释放锁。
当使用 ReentrantLock 时需要手动获取和释放锁,否则会导致死锁或其他问题。因此建议使用 try…finally 结构来保证锁的正确释放:

独占锁:

独占锁是同步锁的一种。同步锁是一种在多线程环境下同步访问共享资源的机制,其主要目的是为了避免多个线程同时对共享资源进行读写操作而导致的数据不一致性问题。独占锁是同步锁的一种,它在同一时间只允许一个线程访问共享资源,其他线程必须等待当前线程释放锁之后才能访问共享资源。

在Java中实现独占锁的方式是通过synchronized关键字或者ReentrantLock类进行实现。当一个线程获得了锁之后,其他线程就必须等待它释放锁之后才能尝试获取锁。独占锁的使用可以有效地避免多个线程同时访问共享资源而导致的数据不一致性问题,但是也会带来一定的性能损失和死锁的风险,需要在实际应用中进行权衡和优化。

synchronized 关键字:

实现方式:方法用 synchronized关键字修饰,这个方法就算是加锁了,可以叫实例锁、对象锁。这个方法在任意时刻只能允许一个线程访问。

流程理解: A线程想调用这个方法,就必须先获取到这个实例关联的锁(对象锁、实例锁),获取到后才能执行方法里面的代码,其他线程如果也想访问这个方法,但是这个方法已经被A线程获取,其他线程就会被阻塞,直到A线程执行完,释放锁之后,其他线程才能获取锁。

锁的获取方式:是通过Java虚拟机底层的监视器机制实现的,不需要我们写代码,在我们去调用这个方法的时候,底层自己就会去获取锁,如果已经被其他线程获取了,就会进行等待,直到锁被释放。

当线程A调用一个被synchronized修饰的方法,但是该锁已经被其他线程占用时,线程A会进入到对象的monitor状态,等待获取锁的机会。每个对象都拥有monitor这个监视器锁。monitor是用来协调多个线程对该对象的访问。通过synchronized关键字修饰的方法或者代码块就是使用该对象的monitor监视器锁来实现锁功能的。

在等待的过程中,线程A会被放置在对象的monitor里面的等待队列中,等待获取锁的机会。当该锁被释放时,Monitor就会通知等待队列中的一个或多个线程,来争夺锁的获取。如果这个时候线程A争到这个锁,就可以执行被synchronized关键字修饰保护的代码,否则就继续回到等待队列等待,直到获取到锁为止。

释放锁:

线程A执行完按方法里面的代码后,会自动释放锁的(当线程A执行完同步代码块之后,虚拟机会自动将获取锁的标识清除)。

线程A也可以调用wait()方法主动释放锁。

如果线程A在执行完synchronized修饰的代码块之后,没有通知(通过调用notify()notifyAll()方法)其它等待的线程,那么其它线程就没办法知道锁什么时候会被释放,就会无限期的等待下去。

相关文章:

乐观锁和悲观锁

目录 悲观锁:乐观锁:CAS算法:版本号机制:write_condition 机制:时间戳:ReentrantLock 类: 独占锁:synchronized 关键字: 悲观锁: 1、理解:总是假设最坏的情况…...

用 pytorch 训练端对端验证码识别神经网络并进行 C++ 移植

文章目录 前言安装安装 pytorch安装 libtorch安装 opencv(C) 准备数据集获取训练数据下载标定 编码预分析 数据集封装格式 神经网络搭建神经网络训练神经网络测试神经网络预测C 移植模型转换通过跟踪转换为 Torch Script通过注解转换为 Torch Script 编写…...

leetcode 739. 每日温度、496. 下一个更大元素 I

739. 每日温度 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。 示例 1: …...

Photon——Fusion服务器(Failed to find entry-points:System.Exception: )

文章目录 前言解决方案:1.报警信息如下2.选择3d urp3.引入Fusion之后选择包管理,点击Burst中的Advanced Project Settings4.勾选两个预设选项5.引入官网unity.burst6.更新后报警消失总结前言 制作局域网游戏,出现未找到进入点报警 Failed to find entry-points 解决方案: …...

双十一必买好物,这四款好物你值得拥有

随着科技的不断发展,智能家电已经成为我们生活中不可或缺的一部分。在双十一期间,各大品牌都会推出各种优惠活动,以更优惠的价格购买到心仪的智能家电。比如智能超声波清洗机,智能门锁,它们不仅提高了我们的生活质量&a…...

视频号视频如何下载(WeChatVideoDownloader)

背景介绍 最近需要一个视频号里面的视频进行宣传用,网上找了很多方法都不行,特别是下载抓包工具Fiddler,然后监控HTTPS请求的,截取URL把URL中20302改成20304,再用IDM工具下载对应的资源,最后修改后缀名.mp…...

【Java-框架-SpringMVC】(01) SpringMVC框架的简单创建与使用,快速上手 - 简易版

前言 【描述】 "SpringMVC"框架的简单创建与使用,快速上手; 【环境】 系统"Windows",软件"IntelliJ IDEA 2021.1.3(Ultimate Edition)";“Java版本"1.8.0_202”,“Spring"版…...

【计算机网络】UDP/TCP协议

文章目录 :peach:1 UDP协议:peach:1.1 :apple:UDP协议端格式:apple:1.2 :apple:UDP的特点:apple:1.3 :apple:UDP的缓冲区:apple:1.4 :apple:UDP使用注意事项:apple:1.5 :apple:基于UDP的应用层协议:apple: 2 :peach:TCP协议:peach:2.1 :apple:TCP协议端格式:apple:2.2 :apple:确…...

【前端设计模式】之享元模式

享元模式是一种结构型设计模式,它通过共享对象来减少内存使用和提高性能。在前端开发中,享元模式可以用于优化大量相似对象的创建和管理,从而提高页面的加载速度和用户体验。 享元模式特性 共享对象:享元模式通过共享相似对象来…...

C++前缀和算法:合并石头的最低成本原理、源码及测试用例

本文涉及的基础知识点 C算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 动态规划,日后完成。 题目 有 n 堆石头排成一排,第 i 堆中有 stones[i] 块石头。 每次 移动 需要将 连续的 k 堆石头合并为一堆,而…...

maven 安装本地jar失败 错误指南

Maven 安装本地 jar 失败 安装命令: mvn install:install-file -Dfile文件路径地址 -DgroupIdcom.allinpay.sdk -DartifactIdtop-sdk-java -Dversion1.0.5 -Dpackagingjar 错误描述 : Unknown lifecycle phase “.allinpay.sdk”. You must specify a valid lifecycle phase o…...

【Spring Boot 源码学习】HttpEncodingAutoConfiguration 详解

Spring Boot 源码学习系列 HttpEncodingAutoConfiguration 详解 引言往期内容主要内容1. CharacterEncodingFilter2. HttpEncodingAutoConfiguration2.1 加载自动配置组件2.2 过滤自动配置组件2.2.1 涉及注解2.2.2 characterEncodingFilter 方法2.2.3 localeCharsetMappingsCus…...

uni-app--》基于小程序开发的电商平台项目实战(七)完结篇

🏍️作者简介:大家好,我是亦世凡华、渴望知识储备自己的一名在校大学生 🛵个人主页:亦世凡华、 🛺系列专栏:uni-app 🚲座右铭:人生亦可燃烧,亦可腐败&#xf…...

手写banner切换方式

<template><!-- banner轮播切换 --><div class"banner-wrapper"><div class"banner-info"><ul class"box" ref"box"><li v-for"(item, index) in bannerList" :key"index">&…...

技术文档工具『Writerside』抢鲜体验

前言 2023 年 10 月 16 日&#xff0c;JetBrains 宣布以早期访问状态推出 Writerside&#xff0c;基于 IntelliJ 平台的 JetBrains IDE&#xff0c;开发人员可使用它编写、构建、测试和发布技术文档&#xff0c;可以作为 JetBrains IDE 中的插件使用&#xff0c;也可以作为独立…...

Centos磁盘爆满_openEuler系统磁盘爆满清理方法---Linux工作笔记060

磁盘爆满,监控部门就会报警,报警就要处理,但是程序员并不擅长做运维的工作,记录一下把...以后用到会方便: 使用df -h命令可以看到,对应的磁盘占用情况,这里我的/dev/mapper/openeuler-root这个目录 占用的磁盘比较多,到了百分之95了.. 往往就是这个跟目录,我这里/data目录是自…...

dubbo启动提示端口号已经被占用

本地dubbo项目启动提示&#xff1a; java.lang.IllegalStateException: Failed to load ApplicationContext at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:132) at org.sp…...

LeetCode每日一题——2678. Number of Senior Citizens

文章目录 一、题目二、题解 一、题目 You are given a 0-indexed array of strings details. Each element of details provides information about a given passenger compressed into a string of length 15. The system is such that: The first ten characters consist o…...

按摩 推拿上门服务小程序源码 家政上门服务系统源码

按摩 推拿上门服务小程序源码 家政上门服务系统源码 上门服务系统是一款基于互联网和移动应用的高端家政服务预订平台&#xff0c;它集成了用户、服务员、客户三方的需求于一体&#xff0c;为广大市民提供方便、高效、安全、舒适的家居服务体验&#xff0c;让你在家当皇帝&…...

排列排序问题---2023 年华中科技大学程序设计竞赛新生赛

解析&#xff1a; 将序列分为多段&#xff0c;每段必须连续并且单调&#xff0c;输出段数-1即可 #include<bits/stdc.h> using namespace std; #define int long long const int N1e65; int n,a[N]; signed main(){scanf("%lld",&n);for(int i1;i<n;i)…...

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周&#xff0c;有很多同学在写期末Java web作业时&#xff0c;运行tomcat出现乱码问题&#xff0c;经过多次解决与研究&#xff0c;我做了如下整理&#xff1a; 原因&#xff1a; IDEA本身编码与tomcat的编码与Windows编码不同导致&#xff0c;Windows 系统控制台…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

日语AI面试高效通关秘籍:专业解读与青柚面试智能助攻

在如今就业市场竞争日益激烈的背景下&#xff0c;越来越多的求职者将目光投向了日本及中日双语岗位。但是&#xff0c;一场日语面试往往让许多人感到步履维艰。你是否也曾因为面试官抛出的“刁钻问题”而心生畏惧&#xff1f;面对生疏的日语交流环境&#xff0c;即便提前恶补了…...

基于Flask实现的医疗保险欺诈识别监测模型

基于Flask实现的医疗保险欺诈识别监测模型 项目截图 项目简介 社会医疗保险是国家通过立法形式强制实施&#xff0c;由雇主和个人按一定比例缴纳保险费&#xff0c;建立社会医疗保险基金&#xff0c;支付雇员医疗费用的一种医疗保险制度&#xff0c; 它是促进社会文明和进步的…...

【JVM】- 内存结构

引言 JVM&#xff1a;Java Virtual Machine 定义&#xff1a;Java虚拟机&#xff0c;Java二进制字节码的运行环境好处&#xff1a; 一次编写&#xff0c;到处运行自动内存管理&#xff0c;垃圾回收的功能数组下标越界检查&#xff08;会抛异常&#xff0c;不会覆盖到其他代码…...

CentOS下的分布式内存计算Spark环境部署

一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架&#xff0c;相比 MapReduce 具有以下核心优势&#xff1a; 内存计算&#xff1a;数据可常驻内存&#xff0c;迭代计算性能提升 10-100 倍&#xff08;文档段落&#xff1a;3-79…...

Java-41 深入浅出 Spring - 声明式事务的支持 事务配置 XML模式 XML+注解模式

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持续更新中&#xff01;&#xff08;长期更新&#xff09; 目前2025年06月05日更新到&#xff1a; AI炼丹日志-28 - Aud…...

自然语言处理——Transformer

自然语言处理——Transformer 自注意力机制多头注意力机制Transformer 虽然循环神经网络可以对具有序列特性的数据非常有效&#xff0c;它能挖掘数据中的时序信息以及语义信息&#xff0c;但是它有一个很大的缺陷——很难并行化。 我们可以考虑用CNN来替代RNN&#xff0c;但是…...

蓝桥杯3498 01串的熵

问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798&#xff0c; 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...