案例突破——悲观锁和乐观锁

news2024/9/20 16:50:30

悲观锁和乐观锁

  • 一、背景介绍
  • 二、悲观锁和乐观锁
    • 什么是悲观锁
    • 什么是乐观锁
  • 三、 在项目中如何使用悲观锁和乐观锁
    • 在项目中使用悲观锁
      • 实体结构
      • 实体对象的xml配置文件
      • 对应生成的表结构
      • 往表中初始化数据
      • 运行之后的结果
      • 模拟触发悲观锁的条件
      • 核心代码
    • 在项目中使用乐观锁
      • 实体结构(添加了version)
      • 实体对应的xml配置文件
      • 数据库表结构
      • 往表中初始化数据
      • 运行之后的结果
      • 实现的核心代码
      • 触发乐观锁条件
  • 三、 什么情况下使用悲观锁什么情况下使用乐观锁
    • 悲观锁的使用场景
    • 乐观锁的使用场景
  • 四、总结

一、背景介绍

悲观锁和乐观锁的出现是为了解决并发编程中的竞态条件和数据一致性问题。
本篇博客主要介绍悲观锁和乐观锁的基本概念,以及如何使用悲观锁和乐观锁应用到项目中,以及悲观锁和乐观锁的使用场景。

二、悲观锁和乐观锁

什么是悲观锁

悲观锁(Pessimistic Locking):对于并发控制策略采取悲观策略,认为并发访问会导致冲突。为了避免冲突悲观锁会在访问数据之前先对其进行加锁。确保同一时间只有一个线程能够对数据进行访问。
一般的悲观锁实现方式是使用数据库中的行级锁,和表级锁。当然jdk提供的synchronized 也是一种悲观锁。

什么是乐观锁

乐观锁(Optimistic Locking),对于并发控制策略采取乐观策略。它认为在数据被修改期间不会有其他线程对其进行修改。乐观锁在读取数据时不会进行加锁,而在更新数据时检查是否有其他线程对数据进行了修改。
一般乐观锁会使用版本号或者时间戳,来表示数据的版本,以便在更新的时候进行校验。
如果在校验时发现了数据已经被修改,则表示发送了冲突,需要进行相应的处理,例如重试、或者回滚。

乐观锁适用于读取操作频繁、冲突较少的场景,因为它避免了不必要的加锁操作,提高了并发性能。然而,如果冲突频繁发生,乐观锁可能需要进行多次重试,可能会降低性能。

三、 在项目中如何使用悲观锁和乐观锁

在项目中使用悲观锁

环境:项目架构为SSH架构,通过使用Hibernate的特性来支持悲观锁。

实体结构

package com.wangwei.hibernate;

public class Inventory {

    private String itemNo;
    
    private String itemName;
    
    private int quantity;

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public String getItemNo() {
        return itemNo;
    }

    public void setItemNo(String itemNo) {
        this.itemNo = itemNo;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }
}

实体对象的xml配置文件

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.wangwei.hibernate.Inventory" table="t_inventory">
        <id name="itemNo">
            <generator class="assigned"/>
        </id>
        <property name="itemName"/>
        <property name="quantity"/>
    </class>
</hibernate-mapping>

对应生成的表结构

在这里插入图片描述

往表中初始化数据

package com.wangwei.hibernate;

import org.hibernate.Session;

public class InitData {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            Inventory inv = new Inventory();
            inv.setItemNo("1001");
            inv.setItemName("三鹿奶粉");
            inv.setQuantity(1000);
            session.save(inv);
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }

}

运行之后的结果

在这里插入图片描述

模拟触发悲观锁的条件

这段代码Inventory inv = (Inventory)session.load(Inventory.class, “1001”, LockMode.UPGRADE);中的LockMode.UPGRADE是使用了Hibernate的悲观锁,对查询的这条数据添加了行级锁。

核心代码

package com.wangwei.hibernate;

import org.hibernate.LockMode;
import org.hibernate.Session;

import junit.framework.TestCase;

public class PessimisticLockingTest extends TestCase {

    public void testLoad1() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            Inventory inv = (Inventory)session.load(Inventory.class, "1001", LockMode.UPGRADE);
            System.out.println("opt1-->itemNo=" + inv.getItemNo());
            System.out.println("opt1-->itemName=" + inv.getItemName());
            System.out.println("opt1-->quantity=" + inv.getQuantity());
            
            inv.setQuantity(inv.getQuantity() - 200);
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }
    
    public void testLoad2() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            Inventory inv = (Inventory)session.load(Inventory.class, "1001", LockMode.UPGRADE);
            System.out.println("opt2-->itemNo=" + inv.getItemNo());
            System.out.println("opt2-->itemName=" + inv.getItemName());
            System.out.println("opt2-->quantity=" + inv.getQuantity());
            
            inv.setQuantity(inv.getQuantity() - 200);
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }            
    
}

现在我们对testLoad1方法中的session.getTransaction().commit();打上断点。并进行运行
可以从图中看到,在查询语句后面有for update语句这就是添加上了行级锁(在没有提交事务之前,其他人无法操作这条数据,只有等待事务提交之后,才能操作这条数据)并且我们已经将这条数据查询出来了。
在这里插入图片描述
这个时候我们再运行testLoad2方法,通过图片我们可以看到并没有往下面进行执行,处于等待状态。这是由于这条记录被锁住了并且有其他人正在使用。之后等其他人使用完之后才能对这条数据进行操作。
在这里插入图片描述我们再执行testLoad1打上断点的语句。可以看到等锁释放之后才会执行testLoad2的方法
在这里插入图片描述
在这里插入图片描述

在项目中使用乐观锁

环境:项目架构为SSH架构,通过使用Hibernate的特性来支持乐观锁。当我们知道实现的思路有原理之后,也可以手动的实现乐观锁的策略。

实现思路:大多数的使用是采用数据版本的方式(version)实现,一般在数据库表中加入一个version字段
在读取数据的时候将version读取出来,在保存或者更新数据的时候判断version的值是否小于数据库中的
version值,如果小于不予更新,否则给予更新。

实体结构(添加了version)

package com.wangwei.hibernate;

public class Inventory {

    private String itemNo;
    
    private String itemName;
    
    private int quantity;
    
    private int version;

    public String getItemName() {
        return itemName;
    }

    public void setItemName(String itemName) {
        this.itemName = itemName;
    }

    public String getItemNo() {
        return itemNo;
    }

    public void setItemNo(String itemNo) {
        this.itemNo = itemNo;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public int getVersion() {
        return version;
    }

    public void setVersion(int version) {
        this.version = version;
    }
}

实体对应的xml配置文件

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="com.wangwei.hibernate.Inventory" table="t_inventory" optimistic-lock="version">
        <id name="itemNo">
            <generator class="assigned"/>
        </id>
        <version name="version"/>
        <property name="itemName"/>
        <property name="quantity"/>
    </class>
</hibernate-mapping>

数据库表结构

在这里插入图片描述

往表中初始化数据

package com.wangwei.hibernate;

import org.hibernate.Session;

public class InitData {

    /**
     * @param args
     */
    public static void main(String[] args) {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            Inventory inv = new Inventory();
            inv.setItemNo("1001");
            inv.setItemName("三鹿奶粉");
            inv.setQuantity(1000);
            session.save(inv);
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }

}

运行之后的结果

在这里插入图片描述

实现的核心代码

package com.wangwei.hibernate;

import org.hibernate.LockMode;
import org.hibernate.Session;

import junit.framework.TestCase;

public class OptimisticLockingTest extends TestCase {

    public void testLoad1() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            Inventory inv = (Inventory)session.load(Inventory.class, "1001");
            System.out.println("opt1-->itemNo=" + inv.getItemNo());
            System.out.println("opt1-->itemName=" + inv.getItemName());
            System.out.println("opt1-->version=" + inv.getVersion());
            System.out.println("opt1-->quantity=" + inv.getQuantity());
            
            inv.setQuantity(inv.getQuantity() - 200);
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }
    
    public void testLoad2() {
        Session session = null;
        try {
            session = HibernateUtils.getSession();
            session.beginTransaction();
            Inventory inv = (Inventory)session.load(Inventory.class, "1001");
            System.out.println("opt2-->itemNo=" + inv.getItemNo());
            System.out.println("opt2-->itemName=" + inv.getItemName());
            System.out.println("opt2-->version=" + inv.getVersion());
            System.out.println("opt2-->quantity=" + inv.getQuantity());
            
            inv.setQuantity(inv.getQuantity() - 200);
            
            session.getTransaction().commit();
        }catch(Exception e) {
            e.printStackTrace();
            session.getTransaction().rollback();
        }finally {
            HibernateUtils.closeSession(session);
        }
    }            
    
}

触发乐观锁条件

先将断点打到代码:

session.getTransaction().commit();

我们先执行testLoad1方法

此时代码运行到session.getTransaction().commit();此时可以看到version为0 并且quantity为1000,此时事务并没有进行提交。
在这里插入图片描述
现在我们再运行testLoad2方法,按照testLoad2方法的执行之后其中的version为1并且quantity为800.
在这里插入图片描述
此时我们再执行testLoad1的断点,可以看到出现了错误,这是因为在ypdate的语句中使用了version作为条件,但是此时的数据库表version已经变为了1,而testLoad1方法开始查出并使用的version为0.所以就会出现错误。

在这里插入图片描述
那么上面是使用Hibernate框架时的情况,那我当我们使用的是JDBC的时候,那么由于修改的这条记录不存在,那么返回的受影响的行数会为0.
我们可以根据这个条件返回给前端,展示给用户,如:您目前的评论已经被覆盖,请重新评论等等的提示语。

三、 什么情况下使用悲观锁什么情况下使用乐观锁

悲观锁的使用场景

  1. 并发冲突概率高:如果在某个场景下,对共享资源的并发冲突概率很高,即多个线程同时修改同一份数据的可能性较大,那么使用悲观锁可以降低冲突风险。
  2. 复杂的操作:如果对共享资源进行复杂的操作,涉及多个步骤或依赖其他资源,使用悲观锁可以确保在整个操作过程中数据的一致性。

乐观锁的使用场景

  1. 并发冲突概率低:如果在某个场景下,并发冲突的概率较低,即多个线程修改同一份数据的可能性很小,那么可以使用乐观锁来提高系统的吞吐量和并发性能。
  2. 读操作远多于写操作:如果在某个场景下,对共享资源的读操作远远多于写操作,那么使用乐观锁可以避免不必要的阻塞,提高系统的性能

四、总结

综上所述:
悲观锁适用于并发冲突概率较高、对数据一致性要求较高的场景,而乐观锁适用于并发冲突概率较低、对性能要求较高的场景。在具体应用中,需要根据实际情况评估并选择适合的锁机制,或者结合使用多种锁机制以达到更好的性能和数据一致性。

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

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

相关文章

【探索 Kubernetes|作业管理 Deployment 篇 系列 12】水平扩展 / 收缩、滚动 / 回滚更新

前言 大家好&#xff0c;我是秋意零。 在上一篇中&#xff0c;我们介绍了控制器的基本设计思想&#xff1a;控制器模式。通过这个 “控制器模式” 我们来看看 Deployment 是如何依靠它来实现的。 最近搞了一个扣扣群&#xff0c;旨在技术交流、博客互助&#xff0c;希望各位…

第40步 深度学习图像识别:DenseNet201建模(Tensorflow)

基于WIN10的64位系统演示 一、写在前面 &#xff08;1&#xff09;DenseNet201 DenseNet201是一种深度卷积神经网络&#xff0c;是DenseNet网络的一种变体。DenseNet&#xff0c;全称Dense Convolutional Network&#xff08;密集卷积网络&#xff09;&#xff0c;是由Faceb…

【VC 7/8】vCenter Server 更新(小版本升级)Ⅱ—— 使用 Shell 命令行更新 vCenter Server

目录 2. 使用 Shell 升级 vCenter Server&#xff08;1&#xff09;下载更新 ISO 镜像&#xff08;2&#xff09;挂载 ISO 镜像&#xff08;3&#xff09;验证 ISO 镜像已被挂载通过VAMI 更新界面将ISO 挂载到 VC 的文件系统 &#xff08;4&#xff09;更新 VC | 安装 vCenter …

Elasticsearch:如何通过 3 个简单步骤从 Elastic 数据中删除个人身份信息

作者&#xff1a;Peter Titov 对于任何组织来说&#xff0c;个人身份信息 (Personally Identifiable information, PII) 合规性都是一个日益严峻的挑战。 无论你是在电子商务、银行、医疗保健还是其他数据敏感的领域&#xff0c;PII 都可能会在无意中被捕获和存储。 拥有结构化…

丰田汽车投资人要求董事长下台

&#x1f699; 丰田电动车推广不力&#xff0c;股东要求董事长下台 Toyota faced down two proxy votes at its annual general meeting. In an unusual challenge to the management of a Japanese company, activist investors in America and Europe recommended voting aga…

跨境电商产品的评价怎么获取?

对于在亚马逊、沃尔玛、eBay、Wish、Newegg、速卖通、阿里国际站、Shopee、Lazada、Temu、乐天、Toktok、Joom、Ozon等跨境电商平台的卖家来说&#xff0c;产品评价和补单&#xff08;增加订单数&#xff09;是一个常见但至关重要的话题 优质的产品评价可以向潜在买家展示我们…

想学习大数据,主要学什么?

什么是大数据 什么是“大数据”呢&#xff1f;如果从字面意思来看&#xff0c;大数据指的是巨量数据。那么可能有人会问&#xff0c;多大量级的数据才叫大数据&#xff1f;不同的机构或学者有不同的理解&#xff0c;难以有一个非常定量的定义&#xff0c;只能说&#xff0c;大…

【技术干货】高精度室内定位方案,影响UWB定位精度的因素分析

物联网时代&#xff0c;室内定位已然成为物联网建设的技术纽带&#xff0c;想要真正发挥位置数据的价值&#xff0c;就需要采集的位置数据有足够精度。基于UWB技术的厘米级UWB高精度室内定位方案已广泛应用于物联网各行业领域的人员定位及资产管理。本篇小编就来带大家了解一下…

QGIS 3D功能操作说明

QGIS可以轻松快速地创建 3D 地图和可视化。可用于3d数据效果浏览及与2D数据的对比及数据的打印输出。具体功能如下。 1.在 QGIS 中&#xff0c;您可以通过几个简单的步骤创建 3D 模型。 (1)在QGIS中添加3D 数据的数据层&#xff0c;例如DEM&#xff0c;以供3D功能使用。 …

快速下载操作系统镜像文件-ubuntu-centos

一键搞定镜像文件下载 操作方式 链接地址&#xff1a;阿里云镜像文件链接地址 点击【OS镜像】弹框中选择相应版本 弹框中选择好相应的发行版本后点击【下载】即可

将数组内的元素变为指定格式的字符串类型numpy.char.mod()方法

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将数组内的每个元素 修改为指定格式的字符串 numpy.char.mod() [太阳]选择题 关于以下代码说法错误的一项是? import numpy as np a np.array([1, 2, 3]) print("【显示】a ",a) p…

前端Vue自定义导航栏菜单 定制左侧导航菜单按钮 中部logo图标 右侧导航菜单按钮

前端Vue自定义导航栏菜单 定制左侧导航菜单按钮 中部logo图标 右侧导航菜单按钮&#xff0c; 下载完整代码请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13152 效果图如下&#xff1a; # cc-navHeader #### 使用方法 使用方法 在page.json设…

轻量级的深度学习框架Tinygrad

Tinygrad是一个轻量级的深度学习库&#xff0c;它提供了一种简化和直观的方法来理解和实现神经网络。在本文中&#xff0c;我们将探讨Tinygrad及其主要功能&#xff0c;以及它如何成为那些开始深度学习之旅的人的有价值的工具。 什么是Tinygrad? Tinygrad是一个开源的深度学习…

Redis持久化机制介绍

Redis持久化 1.Redis持久化2.Redis 的持久化机制是什么&#xff1f;各自的优缺点&#xff1f;2.1.RDB&#xff1a;是Redis DataBase缩写快照2.2.AOF&#xff1a;持久化2.3.AOF和RDB优缺点是什么&#xff1f; 3. 如何选择合适的持久化方式4.Redis持久化数据和缓存怎么做扩容&…

[Pytorch]Broadcasting广播机制

文章目录 Broadcasting广播机制BroadcastableBroadcasting Broadcasting广播机制 Broadcasting机制用于在不同维度的张量进行运算时进行维度的自动增加与扩展&#xff0c;Broadcasting机制使用的前提是两个参与运算的张量是可broadcastable的。 Broadcastable 怎样的两个向量…

【libdatachannel】pycharm运行streamer的信令服务及streamer与js客户端联调1

一 信令服务&#xff1a;启动py服务器 ssl必须额外指定 # Usage: ./server.py [[host:]port] [SSL certificate file]文档给出了服务的启动命令&#xff1a; python3 -m http.server --bind 127.0.0.1 8080 直接运行&#xff1a; python的信令服务 #!/usr/bin/env python # # …

图的广度优先遍历和深度优先遍历

前言&#xff1a;在上一篇博客我们学习了图的基本操作&#xff0c;包括图的建立、结点插入与删除等操作&#xff0c;怎么判断我们建立的图是否正确&#xff0c;很简单把它输出出来就是&#xff0c;但是如何输出它&#xff0c;这就是图的遍历问题了。 一.图的遍历 图的遍历是指…

初识C语言的static关键字(修饰局部变量、全局变量和函数)

目录 学习目标 1.static 修饰局部变量 2.static 修饰全局变量 3.static 修饰函数 学习目标 static修饰局部变量static修饰全局变量static修饰函数 1.static 修饰局部变量 &#xff08;1&#xff09;static修饰局部变量后&#xff0c;这时局部变量就是静态的局部变量。 &am…

光模块安规认证简介

背景 认证是指由认证机构证明产品、服务、管理体系符合相关技术规范的强制性要求或者标准的合格评定活动。其中产品认证是通过对产品的不同层级认证实现各级材料的可追溯性。认证按照内容分类大致包括&#xff1a;安全、电磁兼容&#xff08;EMC&#xff09;和环保等。按照必要…

PyTorch 中通道在最后的内存格式(beta)

PyTorch 中通道在最后的内存格式&#xff08;beta&#xff09; 什么是通道在最后 通道在最后的内存格式是在保留内存尺寸的顺序中对 NCHW 张量进行排序的另一种方法。 通道最后一个张量的排序方式使通道成为最密集的维度&#xff08;又称为每像素存储图像&#xff09;。 例如…