当前位置: 首页 > 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)…...

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …...

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...

Vue记事本应用实现教程

文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展&#xff1a;显示创建时间8. 功能扩展&#xff1a;记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件

在选煤厂、化工厂、钢铁厂等过程生产型企业&#xff0c;其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进&#xff0c;需提前预防假检、错检、漏检&#xff0c;推动智慧生产运维系统数据的流动和现场赋能应用。同时&#xff0c;…...

如何在看板中有效管理突发紧急任务

在看板中有效管理突发紧急任务需要&#xff1a;设立专门的紧急任务通道、重新调整任务优先级、保持适度的WIP&#xff08;Work-in-Progress&#xff09;弹性、优化任务处理流程、提高团队应对突发情况的敏捷性。其中&#xff0c;设立专门的紧急任务通道尤为重要&#xff0c;这能…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?

uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件&#xff0c;用于在原生应用中加载 HTML 页面&#xff1a; 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

视频行为标注工具BehaviLabel(源码+使用介绍+Windows.Exe版本)

前言&#xff1a; 最近在做行为检测相关的模型&#xff0c;用的是时空图卷积网络&#xff08;STGCN&#xff09;&#xff0c;但原有kinetic-400数据集数据质量较低&#xff0c;需要进行细粒度的标注&#xff0c;同时粗略搜了下已有开源工具基本都集中于图像分割这块&#xff0c…...