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 {

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    private final String GOODS = "goods";

    @Override
    public 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;
    }

    @Override
    public 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和b

        DeadLockDemo lockDemo2 = new DeadLockDemo(b, c);
        lockDemo2.dead(); // 锁住a和b

        DeadLockDemo 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和b
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();

        new Thread(()->{
            DeadLockDemo lockDemo2 = new DeadLockDemo(b, c);
            lockDemo2.dead(); // 锁住a和b
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }).start();

        new Thread(()->{
            DeadLockDemo lockDemo3 = new DeadLockDemo(c, a);
            lockDemo3.dead(); // 锁住a和b
            try {
                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.滥用锁的问题,以及产生死锁的条件;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

服务器数据恢复-服务器RAID6硬盘故障离线的数据恢复案例

服务器数据恢复环境: 服务器中有一组由6块磁盘组建的RAID6磁盘阵列。服务器作为WEB服务器使用,上面运行了MYSQL数据库以及存放了网站代码和其他数据文件。 服务器故障: 在服务器运行过程中该raid6阵列中有两块磁盘先后离线,但是管…

设计模式之建造者模式

文章目录 盖房项目需求传统方式解决盖房需求传统方式的问题分析建造者模式概述是建造者模式的四个角色建造者模式原理类图建造者模式的注意事项和细节 盖房项目需求 需要建房子:这一过程为打桩、砌墙、封顶房子有各种各样的,比如普通房,高楼…

nginx配置keepalive长连接

nginx之keepalive详解与其配置_keepalive_timeout_恒者走天下的博客-CSDN博客 为什么要有keepalive? 因为每次建立tcp都要建立三次握手,消耗时间较长,所以为了减少tcp建立连接需要的时间,就可以设置keep_alive长连接。 nginx中keep_alive对…

day-30 代码随想录算法训练营 回溯part06

332.重新安排行程 思路&#xff1a;使用unordered_map记录起点机场对应到达机场&#xff0c;内部使用map记录到达机场的次数&#xff08;因为map会进行排序&#xff0c;可以求出最小路径&#xff09; class Solution { public:vector<string>res;unordered_map<stri…

vue3 01-setup函数

1.setup函数的作用: 1.是组合式api的入口2.比beforeCreate 执行更早3.没有this组件实例一开始创建vue3页面的时候是这样的 <template></template> <script> export default{setup(){return{ }} } </script>给容器传参在页面中显示 数据给模板使用,以…

1239. 串联字符串的最大长度;2826. 将三个组排序;2563. 统计公平数对的数目

1239. 串联字符串的最大长度 核心思想&#xff1a;递归&#xff0c;选或者不选&#xff0c;定义dfs(i&#xff0c;pre)表示从i-n的满足要求的arr中选择字符串串联所能获得的最大长度为dfs(i,pre)&#xff0c;pre表示已经选过的字符串所组成的集合。然后就有两种情况选&#xf…

LNMT搭建部署

目录 一、概述 二、Nginx高级配置 三、搭建 一、概述 所谓的LNMT架构指的就是Linux操作系统上部署Nginx web服务器、MySQL数据库服务器、Tomcat中间件服务器。 二、Nginx高级配置 location 精确匹配 ^~ 不用正则的字符串匹配 …

ssm+vue海鲜自助餐厅系统源码和论文

ssmvue海鲜自助餐厅系统源码和论文068 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&…

一个人多店操作?Shopee知虾多店聊聊有哪些优势?

Shopee知虾多店聊聊是一款为Shopee商家提供全面解决方案的应用程序。Shopee知虾多店聊聊主要致力于解决商家在Shopee平台上的客服对接问题。 以下是Shopee知虾多店聊聊的主要功能和优势&#xff1a; 多端同时登录&#xff1a;Shopee知虾多店聊聊支持多个端口同时登录&#xff0…

概念解析 | 量子机器学习:将量子力学与人工智能的奇妙融合

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:量子机器学习。 量子机器学习:将量子力学与人工智能的奇妙融合 量子增强机器学习:量子经典混合卷积神经网络 量子机器学习是量子计算和机器学习的结合,它利用量子力学的特…

复原20世纪复古修仙游戏

前言 在本教程中&#xff0c;我突发奇想&#xff0c;想做一个复古的修仙游戏&#xff0c;考虑到以前的情怀决定做个古老的躺平修仙游戏 &#x1f4dd;个人主页→数据挖掘博主ZTLJQ的主页 个人推荐python学习系列&#xff1a; ☄️爬虫JS逆向系列专栏 - 爬虫逆向教学 ☄️python…

3DS Max中绘制圆锥箭头

3DS Max中绘制圆锥箭头 绘制结果绘制过程步骤一&#xff1a;绘制立体圆锥方法1方法2 步骤二&#xff1a;圆锥体调参&#xff08;模型尺寸设置&#xff09;1圆锥体参数说明2圆锥体参数调整 步骤三&#xff1a;绘制圆柱体步骤四&#xff1a;圆柱体调参步骤五&#xff1a;圆锥与圆…

ES基础操作

1.创建索引 在 Postman 中&#xff0c;向 ES 服务器发 PUT 请求 &#xff1a; http://127.0.0.1:9200/shopping 后台日志 重复发送 PUT 请求添加索引 &#xff1a; http://127.0.0.1:9200/shopping &#xff0c;会返回错误信息 : 2.获取单个索引相关信息 在 Postman 中&#…

C++编辑修改PDF

PDFWriter是一个易于使用的C创建、修改PDF文档的库 1.创建一个PDF文件 #include #include “PDFWriter.h” int main() { std::cout << “Hello World!\n”; PDFWriter pdfWriter; int retpdfWriter.StartPDF(“D:\mytestwriterpdf.pdf”, ePDFVersion13); if (ret eS…

Java实现根据短连接获取1688商品详情数据,1688淘口令接口,1688API接口封装方法

要通过1688的API获取商品详情数据&#xff0c;您可以使用1688开放平台提供的接口来实现。以下是一种使用Java编程语言实现的示例&#xff0c;展示如何通过1688开放平台API获取商品详情属性数据接口&#xff1a; 首先&#xff0c;确保您已注册成为1688开放平台的开发者&#xf…

基于Qt5开发图形界面——WiringPi调用Linux单板电脑IO

Qt5——WiringPi Qt5WiringPi示例教程 Qt5 Qt是一种跨平台的应用程序开发框架。它被广泛应用于图形用户界面&#xff08;GUI&#xff09;开发&#xff0c;可以用于构建桌面应用程序、移动应用程序和嵌入式应用程序。Qt提供了丰富的功能和工具&#xff0c;使开发人员可以快速、高…

JVM知识点(一)

1、JVM基础概念 &#xff08;1&#xff09;JVM、JRE、JDK JRE&#xff1a;JVM基本类库组成的运行环境就是JRE。JVM自己是无法完成一次编译&#xff0c;处处运行的&#xff0c;需要有一个基本类库告诉JVM如何操作运行&#xff0c;如如何操作文件&#xff0c;连接网络等&#x…

行业追踪,2023-08-29

自动复盘 2023-08-29 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

基于数据湖的多流拼接方案-HUDI概念篇

目录 一、为什么需要HUDI&#xff1f; 1. 传统技术选型存在哪些问题&#xff1f; 2. Hudi有什么优点&#xff1f; 基于 Hudi Payload 机制的多流拼接方案&#xff1a; 二、HUDI的应用场景 1. 什么场景适合使用hudi&#xff1f; 2. 什么场景不适合使用hudi&#xff1f; …

【Qt QAxObject】使用 QAxObject 高效任意读写 Excel 表

1. 用什么操作 Excel 表 Qt 的官网库中是不包含 Microsoft Excel 的操作库&#xff0c;关于对 Microsoft Excel 的操作库可选的有很多&#xff0c;包含基于 Windows 系统本身的 ActiveX、Qt Xlsx、xlsLib、LibXL、qtXLS、BasicExcel、Number Duck。 库.xls.xlsx读写平台Qt Xls…