乐观锁、悲观锁及死锁

server/2024/9/24 1:39:36/

乐观锁、悲观锁

1.概念

  • 悲观锁(悲观锁定):具有强烈的独占和排他特性。在整个执行过程中,将处于锁定状态。悲观锁在持有数据的时候总会把资源或者数据锁住,这样其他线程想要请求这个资源的时候就会阻塞,直到等到悲观锁把资源释放为止. (例如:Synchronized和ReetrantLock)

  • 乐观锁(乐观锁定):乐观锁机制采取了更加宽松的加锁机制。乐观锁在读取时不会上锁,但是乐观锁在进行写入操作的时候会判断当前数据是否被修改过。

    (例如:JAVA中的Stamp锁定和原子整型)

2.锁的使用----读写

ReentranLock

保证了只有一个线程可以执行临界区代码

问题:任何时刻,只允许一个线程执行,不是读线程,就是写线程

👏改进一下:允许多个线程同时读,但只有一个线程在写,其他线程就必须等待

ReadWriteLock

  • 只允许一个线程写入(其他线程既不能写入,也不能读取)

  • 没有写入时,多个线程允许同时读(提高性能)

java"> public class ReadWriteLockTest {private final ReadWriteLock lock = new ReentrantReadWriteLock();private final Lock readLock = lock.readLock();private final Lock writeLock = lock.writeLock();​private int[] counts = new int[10];public void inc(int index){writeLock.lock();try {counts[index]+=1;} finally {writeLock.unlock();}}public int[] get(){readLock.lock();try {return Arrays.copyOf(counts,counts.length);} finally {readLock.unlock();}}}

在读取时,多个线程可以同时获取读锁,大大提高了并发读的执行效率。

问题:如果有线程正在读,写线程需要等待读线程释放锁后才能获取写锁,即读的过程中不允许写.

👏改进一下:读的过程中也允许获取写锁写入!

StampedLock

读的数据就可能不一致,需要一点额外的代码来判断读的过程中是否有写入。所以,这种读锁是一种 乐观锁

java">public class Test2 {private final StampedLock stampedLock = new StampedLock();private double x;private double y;public void move(double deltaX,double deltaY){long stamp = stampedLock.writeLock();​try {x += deltaX;y += deltaY;} finally {stampedLock.unlock(stamp);}}public double distanceFromOrigin(){//假设下面两行代码不是原子操作//假设x,y =(100,200)​//获取读锁(乐观锁)long stamp = stampedLock.tryOptimisticRead();​double currentX =x;//此处已读取到x=100,如果没有写入,读取是正确的(100,200)​double currentY =y;//此处已读取到y,如果没有写入,读取是正确的(100,200)//如果有写入,读取是错误的(100,400)​//检查乐观锁的版本号值(stamp)是否一致if(!stampedLock.validate(stamp)){//获取读锁(悲观锁)stamp = stampedLock.tryReadLock();//重新获取try {currentY=y;currentX=x;} finally {stampedLock.unlock(stamp);}}//计算return Math.sqrt(currentX*currentX+currentY*currentY);}}

和ReadWriteLock相比,写入的加锁是完全一样的,不同的是读取

步骤:

1.通过Try OptimisTicRead()获取一个乐观读锁,并返回版本号。

2.进行读取,读取完成后,我们通过验证validate()去验证版本号,如果在读取过程中没有写入,版本号不变,验证成功,继续后续操作.如果在读取过程中有写入,版本号会发生变化,验证将失败。

3.当验证失败时,再通过ReadLock()获取悲观锁再次读取。(由于写入的概率不高,程序在绝大部分情况下可以通过乐观读锁获取数据,极少数情况下使用悲观读锁获取数据)。

所以,StampeLock把读锁细分为乐观读和悲观读,能进一步提升并发效率。

但这也是有代价的:

一是代码更加复杂。

二是Stamp锁定是不可重入锁,不能在一个线程中反复获取同一个锁。

死锁

1.概念

多个线程在运行中,都需要获取对方线程所持有的锁(资源),导致处于长期无限等待的状态。

死锁发生后,只能通过强制结束JVM进程来解决死锁。

java">public class Test3 {public static void main(String[] args) {DeadLock deadLock = new DeadLock();Thread t1 = new Thread(() -> {try {deadLock.add();} catch (InterruptedException e) {e.printStackTrace();}});Thread t2 = new Thread(() -> {deadLock.dec();});t1.start();t2.start();​}}class DeadLock{//两把锁private static Object lockA = new Object();private static  Object lockB = new Object();​public void add() throws InterruptedException {synchronized (lockA){//获得lockA的锁Thread.sleep(100);//线程休眠synchronized (lockB){System.out.println("执行add()");}//释放lockB的锁}//释放lockA的锁}public void dec(){synchronized (lockB){//获得lockB的锁synchronized (lockA){//获得lockA的锁System.out.println("执行dec()");}//释放lockA的锁}//释放lockB的锁}}

2.死锁的条件

产生死锁有四个必要条件:

1.资源互斥:对所分配的资源进行排它性控制,锁在同一时刻只能被一个线程使用;

2.不可剥夺:线程已获得的资源在未使用完之前,不能被剥夺,只能等待占有者自行释放锁:

3.请求等待:当线程因请求资源而阻塞时,对已获得的资源保持不放.

4.循环等待:线程之间的相互等待

3.排查及定位死锁

4.如何避免死锁

1.每次只占用不超过1个锁.

2.按照相同的顺序申请锁.

3.使用信号量


http://www.ppmy.cn/server/121096.html

相关文章

Vue + element-ui实现动态表单项以及动态校验规则

Vue element-ui实现动态表单项以及动态校验规则 情境 项目需要实现一个功能,表单中某个表单项产品id支持动态新增多个产品id表单项,每个产品id表单项都需要有校验规则,校验失败时各自有对应的校验提示 重点分析 表单对象内字段并非固定&…

ECMAScript和JavaScript的区别:解密JavaScript的标准和实现

ECMAScript和JavaScript的区别:解密JavaScript的标准和实现 作为一名程序软件专家,我经常被问到ECMAScript和JavaScript的区别。虽然这两个术语经常被混用,但它们实际上是不同的概念。在本文中,我们将深入探讨ECMAScript和JavaSc…

银河麒麟桌面操作系统V10(SP1)ssh服务安装与配置

在国产化的大背景下,各种国产操作系统逐步进入运维人员的视野,ssh作为linux远程连接工具,是运维人员必需的工具之一。本文主要介绍在银河麒麟桌面操作系统V10(SP1)上安装和配置ssh服务。 准备工作 1、查看操作系统信息 cat /etc/os-release 笔者操作系统为银河麒麟桌面操…

卷积神经网络-数据增强

文章目录 一、概述二、数据增强的类别1. 裁剪2.翻转和旋转3. 随机遮挡4. 图像变换5. 对transforms的选择操作,使数据增强更灵活 三、应用场景四、总结 一、概述 数据增强(也叫数据扩增)的目的是为了扩充数据和提升模型的泛化能力。有效的数据…

图文深入理解SQL语句的执行过程

List item 本文将深入介绍SQL语句的执行过程。 一.在RDBMS(关系型DB)中,看似很简单的一条已写入DB内存的SQL语句执行过程却非常复杂,也就是说,你执行了一条诸如select count(*) where id 001 from table_name的非常简…

双路创新深度学习!TCN-Transformer+LSTM多变量时间序列预测(Matlab)

双路创新深度学习!TCN-TransformerLSTM多变量时间序列预测(Matlab) 目录 双路创新深度学习!TCN-TransformerLSTM多变量时间序列预测(Matlab)效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab…

Git常用命令详解

Git 是一个功能强大的版本控制系统,以下是一些常用的 Git 命令及其解释和案例: 1、配置 git config --global user.name "Your Name":配置全局用户名。 git config --global user.email "emailexample.com:配置全…

yolov8使用强数据增强

yolo强数据增强 在深度学习的训练中,强数据增强(strong data augmentation)通过对训练数据进行更大幅度的随机变换,增强模型的泛化能力,减少过拟合风险。强数据增强可以包括各种随机的图像变换操作,使得模型…