当前位置: 首页 > news >正文

【多线程】可重入锁 Reentrant Lock

【多线程】可重入锁 Reentrant Lock

一、核心定义

可重入锁,顾名思义,是指一个线程在已经持有某个锁的情况下,可以再次成功获取该锁而不会被阻塞。

换句话说,同一个线程可以多次进入被同一把锁保护的代码块。

二、一个生动的比喻

想象一个房间(共享资源)有一把门锁(锁)。

  • 非可重入锁:你(线程)拿着钥匙进入房间后,把门锁上了。如果你在房间里又想开门(再次获取锁),你会发现门从里面也打不开了(线程被阻塞,导致死锁)。这显然不合理。
  • 可重入锁:你拿着钥匙进入房间后,把门锁上了。这个门锁很智能,它认识主人。当你在房间里再次尝试开门时,锁识别出“哦,原来是主人你啊”,就直接让你通过了,并且会在内部记录你进入的次数(重入次数)。你离开房间时,需要出来同样次数,门才会真正锁上。

三、为什么需要可重入锁?

最常见的场景是在递归调用多个方法相互调用的情况下。

让我们看一个非可重入锁会导致问题的例子:

// 假设我们有一个非可重入锁
public class NonReentrantLock {private boolean isLocked = false;public synchronized void lock() throws InterruptedException {while (isLocked) {wait();}isLocked = true;}public synchronized void unlock() {isLocked = false;notify();}
}// 使用这个锁的类
public class Counter {private NonReentrantLock lock = new NonReentrantLock();private int count = 0;public void increment() {try {lock.lock(); // 第一次获取锁count++;doSomethingElse(); // 调用另一个也需要锁的方法} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void doSomethingElse() {try {lock.lock(); // 第二次尝试获取同一个锁// ... 做一些操作} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}
}

问题分析:

  1. 线程A调用 increment(),成功获取锁 (isLocked 变为 true)。
  2. increment() 内部,调用了 doSomethingElse()
  3. doSomethingElse() 也试图获取同一把锁
  4. 由于这是一个非可重入锁,它检查到 isLockedtrue(即使是被自己锁住的),于是线程A在 lock.lock() 这里等待自己释放锁
  5. 这就导致了经典的 自死锁 情况。线程A永远卡在那里,程序无法继续执行。

四、可重入锁是如何实现的?

可重入锁通常通过以下两个属性来实现:

  1. 持有者线程:记录当前是哪个线程持有此锁。
  2. 重入计数器:记录同一个线程获取该锁的次数。

其工作流程如下:

  • 当线程第一次请求锁时,计数器从0变为1,并记录持有者线程。
  • 同一个线程再次请求锁时,计数器简单地递增(比如从1变为2)。
  • 当线程释放锁时,计数器递减。
  • 只有当计数器减回到0时,锁才会被真正释放,其他线程才能获取。

五、在Java中的实现

在Java中,最常用的两种可重入锁是:

  1. synchronized 关键字
    Java内置的 synchronized 关键字实现的锁就是可重入的。这是最常用的方式。

    public class ReentrantExample {public synchronized void methodA() {System.out.println("In methodA");methodB(); // 调用另一个synchronized方法,不会阻塞!}public synchronized void methodB() {System.out.println("In methodB");}
    }
    

    上面的代码可以完美运行,因为 synchronized 是可重入的。

  2. java.util.concurrent.locks.ReentrantLock
    这是Java并发包(java.util.concurrent)中提供的一个显式的可重入锁实现。它比 synchronized 更灵活,提供了更多功能,如尝试获取锁、可中断的锁获取、公平锁等。

    import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final ReentrantLock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock(); // 获取锁try {count++;doSomethingElse(); // 重入} finally {lock.unlock(); // 必须在finally块中释放锁}}public void doSomethingElse() {lock.lock(); // 同一个线程,再次获取同一把锁try {// ... 做一些操作System.out.println("重入成功,当前重入次数: " + lock.getHoldCount());} finally {lock.unlock();}}
    }
    

六、可重入锁的优点

  • 避免自死锁:解决了递归和方法调用链中的锁问题。
  • 逻辑更自然:符合程序员对锁行为的直觉,同一个线程对自己的锁应该有“特权”。

七、总结

特性可重入锁非可重入锁
核心特性同一线程可多次获取同一线程只能获取一次
递归调用支持导致死锁
方法互调支持导致死锁
实现复杂度稍高(需记录线程和计数器)简单
常见例子Java synchronized, ReentrantLock简单的自旋锁(未实现重入)

简单来说,可重入锁是现代多线程编程的标配,它极大地简化了在面向对象编程中复杂的调用关系下的同步代码编写。你在Java中遇到的大多数锁默认都是可重入的。

http://www.dtcms.com/a/511224.html

相关文章:

  • 蓝牙低功耗(BLE)通信的中心设备/外围设备(连接角色)、主机/从机(时序角色)、客户端/服务器(数据交互角色)的理解
  • 3.5 面向连接的传输: TCP
  • 深度学习(10)-PyTorch 卷积神经网络
  • 网站没有做实名认证推广员是干什么的
  • 异步的feign请求报错:No thread-bound request found
  • 北京建设公司网站建设重庆有网站公司
  • YUV实战案例:一个网络摄像头的工作流程(速通)
  • 深入解析SCT分散加载文件
  • AIGC-Fooocus部署实践:从本地手动配置到云端一键启用的深度剖析
  • 数据结构——最小(代价)生成树
  • NumPy的hstack函数详细教程
  • 020数据结构之优先队列——算法备赛
  • 华为OD-23届考研-测试面经
  • 阿里云网站建设步骤wordpress防止频繁搜索
  • 西宁网站建设哪家公司好东莞seo网站推广
  • 2025年AI IDE的深度评测与推荐:从单一功能效率转向生态壁垒
  • OSS存储的视频,安卓和PC端浏览器打开正常,苹果端打开不播放,什么原因?
  • Spark的shuffle类型与对比
  • 【 论文精读】VIDM:基于扩散模型的视频生成新范式
  • CentOS 7 安装指定内核版本与切换内核版本
  • Spring MVC 拦截器interceptor
  • 如何在 CentOS、Ubuntu 和 Debian 云服务器上安装 Python 3
  • 《金融电子化》:构建金融韧性运行安全体系:从灾备管理到主动防御新范式​​
  • spark组件-spark core(批处理)
  • 进行网站建设视频教程装修网站cms
  • 解决Kali虚拟机中VMnet1(仅主机模式)网卡无法获取IP地址的问题
  • Linux驱动开发笔记(十一)——阻塞和非阻塞IO
  • Docker----快速入门
  • 深度学习8-卷积神经网络-CNN概述-卷积层-池化层-深度卷积神经网络-案例:服装分类
  • 厦门做外贸网站国内十大咨询公司排名