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

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

事务的基本特征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();
    }



![img](https://img-blog.csdnimg.cn/img_convert/d8d04bede33dc726b0f0ab3b3eb51df3.png)
![img](https://img-blog.csdnimg.cn/img_convert/3e611c837e7daa1f9c1348e65ebbcffa.png)
![img](https://i-blog.csdnimg.cn/blog_migrate/ddb0b6ee657196fa0d9abc12934a3205.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618631832)**

v-1715791956870)]
[外链图片转存中...(img-5Cn827JA-1715791956871)]
[外链图片转存中...(img-Y4smpRyi-1715791956871)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上软件测试知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化的资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618631832)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值