Java进阶(6)——抢购问题中的数据不安全(非原子性问题) Java中的synchronize和ReentrantLock锁使用 死锁及其产生的条件

目录

  • 引出
  • 场景:大量请求拥挤抢购
    • 事务的基本特征ACID
    • 线程安全的基本特征
  • 加锁(java)
    • synchronized锁
    • ReentrantLock锁
    • 什么是可重入锁?
      • 如何保证可重入
  • 滥用锁的代价?(死锁)
    • 死锁的四个必要条件
    • 死锁的案例
  • 总结

引出


1.大量请求拥挤抢购中的数据不安全问题;
2.事务ACID:原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability);
3.线程安全特征:原子性(Atomicity)可见性(Visibility)有序性(Ordering);
4.java中的锁初步,synchronize锁和ReentrantLock锁使用初步;
5.滥用锁的问题,以及产生死锁的条件;

场景:大量请求拥挤抢购

在这里插入图片描述

在这里插入图片描述

package com.tianju.redis.service.impl;import com.tianju.redis.service.IRushGoodsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;@Service
public class RushGoodsServiceImpl implements IRushGoodsService {@Autowiredprivate StringRedisTemplate stringRedisTemplate;private final String GOODS = "goods";@Overridepublic String rush() {String sNum = stringRedisTemplate.opsForValue().get(GOODS);int nums = Integer.parseInt(sNum);if (nums>0){stringRedisTemplate.opsForValue().set(GOODS, String.valueOf(--nums) );return stringRedisTemplate.opsForValue().get(GOODS);}else {return "error";}}
}
    @PutMapping("/rushJmeter")public void rushJmeter(){String goodsNum = rushGoodsService.rush();System.out.println("goodsNum: "+goodsNum);}

在这里插入图片描述

事务的基本特征ACID

事务是指一组操作被视为一个不可分割的工作单元,要么全部执行成功,要么全部不执行。事务具有以下四个基本特征,通常被称为ACID特性:

在这里插入图片描述

  • 原子性(Atomicity):事务是一个原子操作,要么全部执行成功,要么全部不执行。如果事务中的任何一个操作失败,整个事务将被回滚到初始状态,不会对数据库产生任何影响。

  • 一致性(Consistency):事务在执行前和执行后,数据库的状态必须保持一致。这意味着事务中的操作必须满足数据库的完整性约束,包括唯一性约束、外键约束等。

  • 隔离性(Isolation):事务的执行是相互隔离的,一个事务的操作不会被其他事务所干扰。隔离性确保了并发执行的事务之间不会产生不一致的结果。

  • 持久性(Durability):一旦事务提交成功,其所做的修改将永久保存在数据库中,即使系统发生故障或重启,修改的结果也不会丢失。

这些特性确保了事务的可靠性和一致性。数据库管理系统通过使用日志和锁等机制来实现事务的特性。在设计和实现数据库应用程序时,需要考虑事务的边界和正确使用事务来保证数据的完整性和一致性

线程安全的基本特征

线程安全是指在多线程环境下,对共享资源的访问和操作不会导致数据不一致或产生不可预期的结果。线程安全的基本特征包括:

  • 原子性(Atomicity):对共享资源的操作要么全部执行成功,要么全部不执行,不存在中间状态。即使在多线程环境下,也能保证操作的完整性。简单说就是相关操作不会中途被其他线程干扰,一般通过同步机制实现

  • 可见性(Visibility):一个线程对共享资源的修改对其他线程是可见的。当一个线程修改了共享资源的值后,其他线程能够立即看到最新的值。可见性,是一个线程修改了某个共享变量,其状态能够立即被其他线程知晓,通常被解释为将线程本地状态反映到主内存上,volatile就是负责保证可见性的。

  • 有序性(Ordering):线程的执行顺序与程序的代码顺序一致。即使在多线程环境下,也能保证操作按照预期的顺序执行。是保证线程内串行语义,避免指令重排等。

在这里插入图片描述
解决办法

  • 使用互斥锁(Mutex)或信号量(Semaphore)等同步机制,确保在同一时间只有一个线程能够访问共享资源。

  • 使用原子操作(Atomic Operation)来保证对共享资源的操作是原子的,不会被其他线程中断。

  • 使用volatile关键字来保证共享变量的可见性,确保一个线程对共享变量的修改对其他线程是可见的。

  • 使用线程安全的数据结构或类,这些数据结构或类已经在设计上考虑了多线程环境下的安全性。

加锁(java)

synchronized锁

可重入锁: sychronized ReentrantLock

在这里插入图片描述

synchronized (this.getClass()){}

在这里插入图片描述

ReentrantLock锁

private final ReentrantLock lock = new ReentrantLock(); // 可重入锁

在这里插入图片描述

什么是可重入锁?

当线程获取某个锁后,还可以继续获取它,可以递归调用,而不会发生死锁;

在这里插入图片描述

在这里插入图片描述

如何保证可重入

当一个线程访问同步块并获取锁时,会在对象头和栈帧中的锁记录里存储偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁

如果测试成功,表示线程已经获得了锁。
如果测试失败,则需要再测试一下Mark Word中偏向锁标志是否设置成1:没有则CAS竞争;设置了,则CAS将对象头偏向锁指向当前线程。再维护一个计数器,同个线程进入则自增1,离开再减1,直到为0才能释放

滥用锁的代价?(死锁)

死锁的四个必要条件

循环 A —> B —>C —>A

产生死锁的必要条件是以下四个条件同时满足:

  • 互斥条件(Mutual Exclusion):至少有一个资源被一个进程独占使用,即在一段时间内只能由一个进程访问。

  • 请求与保持条件(Hold and Wait):一个进程在持有至少一个资源的同时,又请求获取其他进程持有的资源。

  • 不可剥夺条件(No Preemption):资源只能由持有者显式地释放,其他进程无法强制剥夺。

  • 循环等待条件(Circular Wait):存在一个进程资源的循环链,每个进程都在等待下一个进程所持有的资源。

当这四个条件同时满足时,就可能发生死锁。如果任何一个条件不满足,就不会发生死锁。

死锁是多线程或多进程并发执行时的一种常见问题,它会导致系统无法继续执行下去,需要通过死锁检测、死锁预防、死锁避免或死锁解除等方法来处理。

死锁的案例

在这里插入图片描述

可能导致死锁

在这里插入图片描述

锁对象ObjLock

package com.tianju.redis.lock;public class ObjLock {private String name;public ObjLock(String name){this.name = name;}@Overridepublic String toString() {return "ObjLock:"+this.name;}
}

加锁释放锁方法DeadLockDemo

package com.tianju.redis.lock;public class DeadLockDemo {private ObjLock a;public ObjLock b;public DeadLockDemo(ObjLock a,ObjLock b){this.a = a;this.b = b;}public void dead(){System.out.println("********"+a+"对象"+b+"对象都加锁**************");System.out.println(a+"--"+b+": "+"准备给"+a+"对象加锁>>");synchronized (a){System.out.println(a+"--"+b+": "+a+"对象加锁成功...");System.out.println(a+"--"+b+": "+"准备给"+b+"对象加锁>>>");synchronized (b){System.out.println(a+"--"+b+": "+b+"对象加锁成功");}System.out.println(a+"--"+b+": "+"释放"+b+"对象的锁");}System.out.println(a+"--"+b+": "+"释放"+b+"对象的锁");System.out.println("****************");}
}

测试方法

package com.tianju.redis.lock;public class TestDeadLock {/*** 一个一个顺序运行*/public static void run(){ObjLock a = new ObjLock("A");ObjLock b = new ObjLock("B");ObjLock c = new ObjLock("C");DeadLockDemo lockDemo1 = new DeadLockDemo(a, b);lockDemo1.dead(); // 锁住a和bDeadLockDemo lockDemo2 = new DeadLockDemo(b, c);lockDemo2.dead(); // 锁住a和bDeadLockDemo lockDemo3 = new DeadLockDemo(c, a);lockDemo3.dead(); // 锁住a和b}/*** 进行线程抢,死锁*/public static void rushRun(){ObjLock a = new ObjLock("A");ObjLock b = new ObjLock("B");ObjLock c = new ObjLock("C");new Thread(()->{DeadLockDemo lockDemo1 = new DeadLockDemo(a, b);lockDemo1.dead(); // 锁住a和btry {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}}).start();new Thread(()->{DeadLockDemo lockDemo2 = new DeadLockDemo(b, c);lockDemo2.dead(); // 锁住a和btry {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}}).start();new Thread(()->{DeadLockDemo lockDemo3 = new DeadLockDemo(c, a);lockDemo3.dead(); // 锁住a和btry {Thread.sleep(3000);} catch (InterruptedException e) {throw new RuntimeException(e);}}).start();}public static void main(String[] args) {
//        run(); // 顺序执行加锁,解锁rushRun(); // 线程进行抢}
}

总结

1.大量请求拥挤抢购中的数据不安全问题;
2.事务ACID:原子性(Atomicity)一致性(Consistency)隔离性(Isolation)持久性(Durability);
3.线程安全特征:原子性(Atomicity)可见性(Visibility)有序性(Ordering);
4.java中的锁初步,synchronize锁和ReentrantLock锁使用初步;
5.滥用锁的问题,以及产生死锁的条件;

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

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

相关文章

基于SpringBoot的员工(人事)管理系统

基于SpringBoot的员工(人事)管理系统 一、系统介绍二、功能展示三.其他系统实现五.获取源码 一、系统介绍 项目名称:基于SPringBoot的员工管理系统 项目架构:B/S架构 开发语言:Java语言 前端技术:BootS…

Go 第三方库引起的线上问题、如何在线线上环境进行调试定位问题以及golang开发中各种问题精华整理总结

Go 第三方库引起的线上问题、如何在线线上环境进行调试定位问题以及golang开发中各种问题精华整理总结。 01 前言 在使用 Go 语言进行 Web 开发时,我们往往会选择一些优秀的库来简化 HTTP 请求的处理。其中,go-resty 是一个被广泛使用的 HTTP 客户端。…

运用亚马逊云科技Amazon Kendra,快速部署企业智能搜索应用

亚马逊云科技Amazon Kendra是一项由机器学习(ML)提供支持的企业搜索服务。Kendra内置数据源连接器,支持快速访问Amazon S3、AmazonRDS、AmazonFSX以及其他外部数据源,帮助用户自动提取文档并建立索引。Kendra支持超过30多种多国语…

【真题解析】系统集成项目管理工程师 2022 年下半年真题卷(案例分析)

本文为系统集成项目管理工程师考试(软考) 2022 年下半年真题(全国卷),包含答案与详细解析。考试共分为两科,成绩均 ≥45 即可通过考试: 综合知识(选择题 75 道,75分)案例分析&#x…

无涯教程-分类算法 - 逻辑回归

逻辑回归是一种监督学习分类算法,用于预测目标变量的概率,目标或因变量的性质是二分法,这意味着将只有两种可能的类。 简而言之,因变量本质上是二进制的,其数据编码为1(代表成功/是)或0(代表失败/否)。 在数学上&…

reactantd(12)动态表单的默认值问题

最近遇到一个需求是有一个表单可以输入各种信息,然后还需要有一个编辑功能,点击编辑的时候需要把当前数据填入到表单里面。在网上查了很多种方法,然后我的思路是使用initialValues搭配setState()使用。默认值都为空,然后点击单条数…

VMware虚拟机---Ubuntu无法连接网络该怎么解决?

在学习使用Linux系统时,由于多数同学们的PC上多是Windows系统,故会选择使用VMware创建一个虚拟机来安装Linux系统进行学习。 安装完成之后,在使用时总是会遇到各种各样的问题。本片随笔就主要针对可能出现的网络问题进行一个总结,…

Git仓库简介

1、工作区、暂存区、仓库 工作区:电脑里能看到的目录。 暂存区:工作区有一个隐藏目录.git,是Git的版本库,Git的版本库里存了很多东西,其中最重要的就是称为stage(或者叫index)的暂存区&#xf…

stm32读写片内flash项目总结(多字节读写tongxindu)

1.flash操作驱动程序 a头文件 #ifndef FLASH_H #define FLASH_H #include “stm32f4xx.h” #define BOARD_NUM_ADDR 0x0800C000 #define STM32_FLASH_BASE 0x08000000 //STM32 FLASH的起始地址 #define FLASH_WAITETIME 50000 //FLASH等待超时时间 //FLASH 扇区的起始地址…

idea --Git Commit Template插件

Git Commit Template是一款免费的IntelliJ IDEA插件,用于提供Git提交模板。该插件可以帮助开发者编写规范的Git提交信息,提高代码管理效率。 首先安装插件: 使用Git Commit Template插件: 注:long description和Breaking changes…

开源文库系统moredoc

什么是 moredoc ? moredoc 中文名 魔豆文库,是基于 golang 开发的类似百度文库、新浪爱问文库的开源文库系统,支持 TXT、PDF、EPUB、MOBI、Office 等格式文档的在线预览与管理,为 dochub 文库(github, gitee )的重构版…

代码随想录算法训练营第四十九天 | 121. 买卖股票的最佳时机,122.买卖股票的最佳时机II

代码随想录算法训练营第四十九天 | 121. 买卖股票的最佳时机,122.买卖股票的最佳时机II 121. 买卖股票的最佳时机122.买卖股票的最佳时机II 121. 买卖股票的最佳时机 题目链接 视频讲解 给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股…

英特尔开始加码封装领域 | 百能云芯

在积极推进先进制程研发的同时,英特尔正在加大先进封装领域的投入。在这个背景下,该公司正在马来西亚槟城兴建一座全新的封装厂,以加强其在2.5D/3D封装布局领域的实力。据了解,英特尔计划到2025年前,将其最先进的3D Fo…

Ubuntu安装RabbitMQ

一、安装 更新系统软件包列表: sudo apt update安装RabbitMQ的依赖组件和GPG密钥: sudo apt install -y curl gnupg curl -fsSL https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc | sudo gpg --dearmo…

网易新财报:游戏稳、有道进、云音乐正爬坡

今年以来,AI大模型的火热程度屡屡攀升,越来越多的企业都加入到了AI大模型的赛场中,纷纷下场布局。而在众多参与者中,互联网企业的身影更是频频浮现,比如,百度、阿里巴巴、腾讯等等。值得一提的是&#xff0…

BI系统框架模型与指标库参考

主数据 :组织|岗位|人员|大区|三大主数据(客户、物料、供应商)财务主数据(科目|成本中心|利润中心|资产)|工作中心|工艺路线 业务数据 :线索|业务机会|合同|订单|采购|生产|发货|物流|财务&#xff0…

Python数据分析 | 各种图表对比总结

本期将带领大家一起对在数据可视化的过程中常用的一些图表进行下总结: 条形图 【适用场景】 适用场合是二维数据集(每个数据点包括两个值x和y),但只有一个维度需要比较,用于显示一段时间内的数据变化或显示各项之间的…

LLMs之Code:Code Llama的简介、安装、使用方法之详细攻略

LLMs之Code:Code Llama的简介、安装、使用方法之详细攻略 导读:2023年08月25日(北京时间),Meta发布了Code Llama,一个可以使用文本提示生成代码的大型语言模型(LLM)。Code Llama是最先进的公开可用的LLM代码任务,并有潜…

《开发实战》11 | 空值处理:分不清楚的null和恼人的空指针

11 | 空值处理:分不清楚的null和恼人的空指针 修复和定位恼人的空指针问题 NullPointerException 是 Java 代码中最常见的异常,最可能出现的场景归为以下5 种: 参数值是 Integer 等包装类型,使用时因为自动拆箱出现了空指针异常…

基于Jenkins自动打包并部署Tomcat环境

基于上一章创建部署 Linux下Jenkins安装 (最新)_学习新鲜事物的博客-CSDN博客 传统网站部署的流程 在运维过程中,网站部署是运维的工作之一。传统的网站部署的流程大致分为:需求分 析-->原型设计-->开发代码-->提交代码--&g…