[Java多线程-基础] 避免线程死锁问题(ReentrantLock的使用)

2023-05-16

 ReentrantLock 的设计初衷是为了提供一种比 synchronized 更加灵活和可控的锁机制。与 synchronized 相比,ReentrantLock 提供了更多的功能,如可重入性、公平锁和中断锁等,使得它在某些场景下更适用。

具体来说,ReentrantLock 可以通过以下方式提供更好的控制和灵活性:

1. 可重入性:允许线程多次获得同一个锁,避免死锁情况的发生。
2. 公平锁:可以实现公平的锁分配机制,避免某些线程长期无法获取到锁而产生的饥饿问题。
3. 可打断:允许使用 interrupt() 方法来中断正在等待获取锁的线程,提高了程序的响应性能力。
4. 条件变量:提供了 Condition 类来支持对线程等待/唤醒操作的高级控制。

由于 ReentrantLock 提供了这些功能,它在某些高并发场景下比 synchronized 更加有优势。


🔓1.可重入性

在下面的示例中,outer()方法获取锁后调用了inner()方法,而inner()方法也会获取同一个锁。由于ReentrantLock是可重入锁,这意味着同一线程可以多次获取锁而不会发生死锁。因此,当inner()方法获取锁时,它并不会被阻塞,而是继续执行并输出"执行内部方法"。当inner()方法执行完毕后,它会释放锁,然后outer()方法也会释放锁

import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {
    private final ReentrantLock lock = new ReentrantLock();

    public void outer() throws InterruptedException {
        lock.lock(); // 获取锁
        try {
            inner(); // 调用内部方法
        } finally {
            lock.unlock(); // 释放锁
        }
    }

    public void inner() throws InterruptedException {
        lock.lock(); // 获取锁
        try {
            System.out.println("执行内部方法");
        } finally {
            lock.unlock(); // 释放锁
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo demo = new ReentrantLockDemo();
        demo.outer(); // 调用外部方法
    }
}

🔓可打断

ReentrantLock 的 lockInterruptibly() 方法提供了可中断的获取锁操作,如果当前线程正在等待获取锁时,可以使用 interrupt() 方法中断该线程等待并唤醒它。这样,当其他线程需要获取同一个锁时,就可以通过 interrupt() 方法打断某些线程的等待,从而避免死锁问题。

需要注意的是,在使用可中断的获取锁操作时,需要对 InterruptedException 异常进行处理,以便及时释放锁和退出线程等待状态。


@Slf4j
public class ReentrantLockDemo {
    private static ReentrantLock lock = new ReentrantLock();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            try {
                log.info("2.尝试获取锁...");
                //如果没有竞争此方法就会获取lock锁对象
                //如果有竞争就会进入阻塞队列, 可以使用其他线程用interrupt唤醒
                lock.lockInterruptibly();
            } catch (InterruptedException e) {
                e.printStackTrace();
                log.info("4.没有获取到锁, 返回");
                return;
            }

            try {
                log.info("尝试获取锁");
            } finally {
                log.info("释放锁~");
                lock.unlock();
            }

        }, "t1");

        log.info("1.率先获取锁");
        t1.start();
        lock.lock();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        log.info("3.唤醒被阻塞的线程");
        t1.interrupt();

    }


}

🔓 锁超时

ReentrantLock 的 tryLock() 方法实现锁的超时控制,以及在多线程场景下如何避免死锁问题,提高程序的健壮性和可靠性。

@Slf4j(topic = "ReentrantLockDemo")
public class ReentrantLockDemo {
    private static ReentrantLock lock = new ReentrantLock();


    //可超时
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            log.info("尝试获取锁..");
            try {
                // 尝试获取锁并设定等待时间为1秒
                if (!lock.tryLock(1, TimeUnit.SECONDS)) {
                    log.info("获取不到锁了, 结束..");
                    return;
                }
            } catch (Exception e) {
                e.printStackTrace();
                log.error("获取不到锁: {}", e);
                return;
            }

            try {
                log.info("获取到锁了");
            } finally {
                log.info("开始释放锁..");
                lock.unlock();
            }
        }, "t1");

        lock.lock();
        log.info("获得到锁了~~~");
        t1.start();
    }

}

代码中创建了一个静态的 ReentrantLock 对象,然后在主线程中获取该锁。创建了一个线程 t1,在该线程中尝试获取这个锁,并设定等待时间为 1 秒。如果在 1 秒内获取到了锁,则打印获取到锁的信息;否则打印获取不到锁的信息并结束线程。

因此,你的代码演示了如何使用 ReentrantLock 的 tryLock(long timeout, TimeUnit unit) 方法设置锁超时时间,避免线程长时间等待锁而引起死锁问题。


🔓 公平锁与非公平锁

Java中的公平锁和非公平锁的主要区别在于对于线程获取锁的顺序的处理方式。

公平锁保证等待时间最久的线程最先获取锁,即按照请求锁的时间顺序来分配锁。而非公平锁则不考虑等待时间的长短,有可能新请求锁的线程会插队抢占已经持有锁的线程的锁。

使用公平锁会使得所有线程都有平等的机会获取锁,但是会增加系统开销和降低吞吐量。而非公平锁则可以提高系统吞吐量,但是可能会导致某些线程长时间无法获取到锁。

Java中可重入锁(ReentrantLock)默认为非公平锁,但可以通过构造函数参数来指定为公平锁。

这里我们看一下源码就一目了然 :

 在这个构造函数中,如果传入的参数fairtrue,则会创建一个FairSync对象作为锁的同步器;否则,会创建一个NonfairSync对象作为锁的同步器。

其中,FairSync实现了公平锁的逻辑,按照等待时间的顺序授予锁的访问权;而NonfairSync实现了非公平锁的逻辑,允许新的请求插队抢占已经持有锁的线程的锁。

这里我们举一个业务的例子 :

      我们以电商网站的库存为例, 在并发场景下大量用户正在进行抢单操作, 这时候需要对 (库存数量) 进行保护, 避免多个用户同时修改库存数据而导致不一致的问题。 就需要用到公平锁 可以保证每个用户获取锁的机会都是一样的。当多个用户同时请求锁时,锁会按照请求锁的时间顺序进行分配,从而保证了访问资源的顺序是公平的。这种方式适用于业务中对资源访问顺序有明确要求的情况。

    另一方面,如果使用非公平锁来保护这些关键资源,可以减少线程阻塞的时间,提高系统的吞吐量。例如,当某个用户请求锁时,如果该锁已经被其他用户持有,那么该用户可以通过插队抢占锁的方式来减少等待时间,从而更快地完成访问资源的操作。这种方式适用于业务中资源的访问顺序没有明确要求,并且希望尽可能提高系统的性能的情况。

 

扫描下方公众号二维码 回复: 多线程 领取多线程面试题 👇 👇 👇

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[Java多线程-基础] 避免线程死锁问题(ReentrantLock的使用) 的相关文章

  • ROS放弃指南2:ROS创建工作空间及五种设置环境变量的方法

    参考链接 xff1a 中国大学MOOC中的机器人操作系统入门 https www icourse163 org course ISCAS 1002580008 古月居博客 www guyuehome com 运行环境 xff1a Ubunt
  • 百科不全书之ROS函数解析

    1 ROS的回调函数 span class token comment 单线程 span ros span class token operator span span class token operator span span clas
  • VsCode 运行后终端没有结果

    最近入手了VsCode很多都还不太清楚 xff0c 稍微记录一下碰到的一点问题 也是第一次写博客 VsCode 运行后终端没有结果 一 运行后终端没有结果二 终端中文乱码问题 一 运行后终端没有结果 在网上试了好多好多解决方法都没有用 xf
  • STM32CubeMX(05) 移植陀螺仪MPU6050的DMP库读取三轴角度,加速度

    文章目录 前言一 MPU6050是什么 xff1f 二 STM32CubeMX配置2 1 IIC配置2 2 开启中断2 3 硬件连接2 4 软件编写 三 导入DMP库3 1 keil配置3 2 添加头文件路径3 3 添加头文件3 4 添加初
  • 基于STM32的智能GPS定位系统(云平台、小程序)

    如需源码或成品可以私我 背景及目标 前阵子 xff0c 准确的说是好几个月前买了一辆电瓶车 xff0c 当时呢因为车停得很随意 xff0c 所以想给小电驴装一个GPS xff0c 一方面是防盗 xff0c 另一方面是为了测速和绘制骑行轨迹
  • 蓝桥杯大赛

    第十一届蓝桥杯单片机比赛心得 前期的准备十月份省赛十一月份国赛错过结果发布 想要做一点事情 xff0c 传承 前期的准备 本次蓝桥杯大赛由于疫情原因延迟了将近7个月举行 xff0c 原先是3月份举行 xff0c 拖到了10月份 xff0c
  • 蓝桥杯模块练习之关闭外设

    蓝桥杯单片机比赛系列1初探关闭外设 关闭LED关闭继电器和蜂鸣器 关闭LED 本节将会介绍板子上的最简单最基础的部分 比赛一般上来需要关闭无关外设 xff0c 蓝桥杯的板子比较特殊 xff0c 51上电默认P0 O1 P2 P3都是高电平
  • 蓝桥杯模块练习之温度传感器DS18B20

    蓝桥杯单片机比赛系列4温度传感器DS18B20 温度传感器DS18B20原理相关电路onewire总线几个需要知道的暂存器和命令 代码解释修改代码自写代码 实现代码 温度传感器DS18B20原理 相关电路 DS18B20遵循onewire总
  • 被锡膏坑了一把

    锡膏 61 锡珠 43 助焊剂 把锡膏放大来看如下图 我是去年买的一罐锡膏 xff0c 138度的 xff0c 用了一两次 xff0c 然后就放在哪里没动它 xff0c 盖子也盖好了 xff0c 没有放冰箱 今年又拿出来用 xff0c 用钢
  • 蓝桥杯模块练习之AD/DA

    蓝桥杯单片机比赛系列6AD DA转换 AD DA原理相关电路pcf8591器件地址 代码解释修改代码AD自写代码ADDA AD DA原理 相关电路 通过pcf8591芯片实现ad转换 板子上ad采集主要采集滑动变阻器的电压值和与光敏电阻串联
  • 蓝桥杯模块练习之EEPROM

    蓝桥杯单片机比赛系列7EEPROM EEPROM原理相关电路AT24C02器件地址 EEPROM自写代码 EEPROM原理 相关电路 有了系列6的基础 xff0c 上手eeprom就简单多了 xff0c 板子上对应的器件是AT24C02 A
  • Openmv学习day1——色块识别

    find blobs函数 image find blobs thresholds roi 61 Auto x stride 61 2 y stride 61 1 invert 61 False area threshold 61 10 pi
  • 蓝桥杯嵌入式模块练习之扩展版MEME

    三轴传感器 PA4 7都不能作为其他用处 xff0c 三周传感器需要使用到这四个引脚资源 当然 xff0c 如果不用中断 xff0c 也可以只结PA4 5 xff0c PA6 7可接到温度传感器和温湿度传感器 这个外设的通信协议也是I2C跟
  • Github Pages 搭建网站

    个人站点 访问 https 用户名 gitub io 搭建步骤 1 创建个人站点 gt 新建仓库 xff08 仓库名必须是 用户名 github io xff09 2 在仓库下新建index heml文件即可 3 Github pages仅
  • 普通io口模拟串口通信

    之前公司在做项目的时候需要用到多串口 xff0c 板载串口资源不足 xff0c 就在网上找相关内容 xff0c 结合自己的理解做出虚拟串口 模拟串口需要用到两个普通io引脚 xff0c 一个定时器 软件串口的实现 IO模拟串口 波特率 xf
  • UART,SPI,IIC,RS232通信时序和规则

    一 UART 1 串口通信方式 2 串口通信步骤 注意 xff1a 串口协议规定 xff0c 闲置时必须是高电平 校验位 xff1a 是使用奇偶校验 停止位必须高电平 一个0和多个0区分是靠掐时间 异步通信 xff1a 时钟各不一样 二 I
  • kvaser pcie can 在ros中使用socketcan开发

    kvaser pcie can 在ros中使用socketcan开发 0 系统配置 Ubuntu 16 04 6 LTS Linux version 4 15 0 45 generic 1 官网下载地址 https www kvaser c
  • 算法训练 - 连接字符串 编程将两个字符串连接起来。例如country与side相连接成为countryside。   输入两行,每行一个字符串(只包含小写字母,长度不超过100);输出一行一个字符

    问题描述 编程将两个字符串连接起来 例如country与side相连接成为countryside 输入两行 xff0c 每行一个字符串 xff08 只包含小写字母 xff0c 长度不超过100 xff09 xff1b 输出一行一个字符串 例
  • 笔记 FreeRtos任务创建失败原因

    问题 使用NXP的S32芯片开发 xff0c 环境是S32DS 2018 xff0c 创建了三个任务 xff0c 最后发现只有一个任务在运行 找问题 S32DS自带了Freertos的分析调试工具 xff0c 打开后可以显示任务的状态 xf
  • 3.提升不同专业能力的差别?

    有段时间没写博客了 今天来谈谈最近工作的一些感悟 首先 我觉得工资和个人能力是成正相关的 这应该是是所有人都认同的吧 如果工资是一个函数的话 也可以说 工资 Y 是一个与个人能力 X 有关的一次函数Y aX b 方然我们不能忽略行业之间的差

随机推荐

  • 网络通讯学习(1)---TCP通讯

    TCP IP四层模型 UDP TCP协议 TCP xff08 The Transmission Control Protocol xff09 xff1a 传输控制协议 UDP TCP协议都属于传输层协议 xff0c 都位于IP协议以上 xf
  • 网络通讯学习(3)-----UDP通讯(仅了解)

    理论 UDP xff08 用户数据报协议 xff09 是一个无连接 xff0c 不可靠的数据传输 xff0c 其特点是简单 xff0c 快捷 相比与TCP xff0c UDP不需要建立连接 xff08 不需connect accept函数
  • WIFI模块不支持MQTT协议,可通过MCU实现

    1 话题原因 我们使用某款WIFI模块 xff0c 在物联网开发时 xff0c 平台端的开发者想要使用MQTT协议 xff0c 但是我们当前使用的模块不支持MQTT协议 xff08 好像ESP8266可以通过重新烧录固件的方式支持 xff0
  • (一) 路径规划算法---Astar与C++可视化在RVIZ的三维点云地图

    Astar与C 43 43 可视化在RVIZ的三维点云地图 文章目录 Astar与C 43 43 可视化在RVIZ的三维点云地图1 功能包介绍2 算法功能包的组成与介绍2 1文件系统组成2 2 头文件说明2 3 源文件说明 3 相关坐标系说
  • SpringSecurity整合OAuth2.0

    SpringSecurity整合OAuth2 一 概述与原理1 1 OAuth2 0 是什么 xff1f 1 2 OAuth2 0中角色解释1 3 OAuth2 0的4中授权模式1 3 1 授权码模式 xff08 重点 xff09 1 3
  • HAL_UART_IRQHandler(UART_HandleTypeDef *huart)里面的中断接收函数

    目录 前言1 UART Receive IT2 HAL UART Receive3 HAL UART Receive IT 前言 看了很长时间串口中断的HAL库 xff0c 最容易混淆的就是函数的名称 xff0c 主要集中在UART Rec
  • 位操作读写寄存器一个字节的多个位

    一 写寄存器多个位 方法一 span class token comment bitStart 目标字节的起始位 length 位长度 data 存放改变目标字节位的值 b 写入后的一个字节值 span u8 mask
  • STM32 电压采集上位机 C#

    工具箱中添加progressBar 添加一个事件函数 xff0c 用于串口接收数据 xff0c 并显示电压值 private void PortDataReceivedEvent object sender SerialDataReceiv
  • cmake使用教程(一)多目录下多个文件的构建

    1 采用 out of source 外部构建多个目录多个文件 这里的文件存储方式如下 xff1a 其中build是构建目录 xff0c 即构建的结果和中间产物都在该目录下 include是包含目录 src是子函数的目录或是依赖文件的目录
  • vue 实现遍历后端接口数据并展示在表格中

    用前端的vue遍历接口 首先就需要有后端的JSON数据 这里可以自己去写接口 可以伪造JSON数据 整理是伪造的JSON数据 34 userId 34 1 34 deptId 34 103 34 userName 34 34 admin 3
  • STM32的存储器映射中的指针操作

    例如 xff1a GPIOB的端口输出数据寄存器ODR的地址是0x4001 0C0C 并且ODR寄存器是32位的 那么我们可以用如下代码对寄存器进行操作 xff1a unsigned int 0x4001 0C0C 61 0xFFFFFFF
  • Mac 启动Redis报错 没有指定的conf文件

    报错如下 xff1a Warning no config file specified span class token punctuation span span class token keyword using span the de
  • java 优化双重for循环

    首先我们要有两个对象分别是 学生信息 和 学生住宿信息 span class token keyword class span span class token class name Student span span class toke
  • 微服务 - gateway网关配置

    server port 10010 网关端口 spring application name gateway 服务名称 cloud nacos server addr localhost 8848 nacos地址 gateway route
  • 如何在手机或平板上编写代码?

    下面给大家推荐一款免费的 在线协作式 基于浏览器的 IDE的在线编程网站 支持语言包括 Java C 43 43 C C JavaScript CSS PHP等50多种主流开发语言 地址 The collaborative browser
  • 羊了个羊, 低配版开源代码来啦~

    前几天朋友圈突然被一个小游戏 羊了个羊 刷屏了 xff0c 出于好奇我也打算小玩一把试试 xff0c 结果没想到上头了 游戏的玩法非常简单 xff0c 类似 消消乐 xff0c 从一堆方块中找到相同图案的 3 个方块并消除即可 但没想到 x
  • MySQL 使用索引和不使用索引的区别(附17W条数据SQL文件)

    MySQL 使用索引可以减少查询的时间 xff0c 而不使用索引的查询会更加耗时 xff0c 因为MySQL需要扫描整个表 此外 xff0c 使用索引可以提高查询的性能 xff0c 同时也可以提高查询的可读性和可维护性 换句话来说 使用索引
  • 如何使用AI来帮你写代码(Cursor使用教程)

    x1f4ac 产品介绍 cursor是一个新的Ide xff0c 它使用Ai来帮助您重构理解调试并使用Cursor编写代码我们的目标是使构建软件的过程更快 更愉快 我们从头开始构建了一个代码编辑器 xff0c 对我们的第一个功能进行了原型设
  • [Java多线程-基础] 如何定位线程中的死锁问题?

    x1f512 死锁代码 下面提供的代码演示了死锁的情况 程序创建了两个线程 xff0c 线程1和线程2 xff0c 它们都试图以不同的顺序获取两个不同的资源 xff0c resource1和resource2 线程1首先获取resource
  • [Java多线程-基础] 避免线程死锁问题(ReentrantLock的使用)

    ReentrantLock 的设计初衷是为了提供一种比 synchronized 更加灵活和可控的锁机制 与 synchronized 相比 xff0c ReentrantLock 提供了更多的功能 xff0c 如可重入性 公平锁和中断锁等