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

深入探秘ReentrantLock的实现与应用:从底层原理到业务场景的实践

1. ReentrantLock概述

ReentrantLockjava.util.concurrent.locks 包中的一个可重入互斥锁,它提供了比 synchronized 更加灵活的锁机制。它的特性包括:

  • 可重入性:同一线程可以多次获得该锁而不会导致死锁。
  • 可定时锁:可以指定超时时间来尝试获取锁。
  • 公平性:可设置为公平锁,保证锁的获取顺序是按照线程请求的顺序。
  • 可中断性:线程在等待锁时可以响应中断。

ReentrantLock 实现了 Lock 接口,并通过内部的 AbstractQueuedSynchronizer (AQS) 来管理锁的获取和释放。


2. ReentrantLock的底层实现

ReentrantLock 通过 AQS(AbstractQueuedSynchronizer) 实现锁的管理,使用了 CAS(Compare and Swap) 来确保锁的原子性操作,保证锁的多线程安全。

  1. 获取锁的流程:当一个线程尝试获取锁时,AQSstate 状态被检查。若 state == 0 表示锁是可用的,线程成功获取锁,并将 state 增加。否则线程进入阻塞队列。
  2. 释放锁的流程:当线程释放锁时,state 递减到 0,并唤醒等待在阻塞队列中的其他线程。
  3. 可重入性:同一个线程可以多次获得锁,每获取一次锁,state 递增;每释放一次锁,state 递减。当 state == 0 时,锁完全释放。

3. ReentrantLock的Java模拟代码

import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockDemo {// 创建一个ReentrantLock实例private final ReentrantLock lock = new ReentrantLock();public void performTask() {// 尝试获取锁lock.lock();try {System.out.println(Thread.currentThread().getName() + " - 获取了锁");// 模拟任务for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + " - 执行任务 " + i);Thread.sleep(1000); // 模拟执行时间}} catch (InterruptedException e) {e.printStackTrace();} finally {// 释放锁lock.unlock();System.out.println(Thread.currentThread().getName() + " - 释放了锁");}}public static void main(String[] args) {ReentrantLockDemo demo = new ReentrantLockDemo();// 创建两个线程Thread t1 = new Thread(() -> demo.performTask(), "线程1");Thread t2 = new Thread(() -> demo.performTask(), "线程2");t1.start();t2.start();}
}

4. 代码解释

  • ReentrantLock的使用lock.lock() 获取锁;lock.unlock() 释放锁。这两个方法分别在 tryfinally 块中使用,确保锁在任务执行完毕后被正确释放。

  • 多线程并发:创建了两个线程 t1t2,它们都试图获取锁并执行 performTask 方法。由于锁的存在,只有一个线程可以同时执行任务,另一个线程则被阻塞,直到锁被释放。

  • 可重入性ReentrantLock 的可重入性允许线程多次获取同一锁,避免死锁问题。虽然在此代码中未使用递归锁,但在复杂场景下,ReentrantLock允许线程在同一方法或不同方法中多次获取锁。


5. 运行结果

线程1 - 获取了锁
线程1 - 执行任务 0
线程1 - 执行任务 1
线程1 - 执行任务 2
线程1 - 执行任务 3
线程1 - 执行任务 4
线程1 - 释放了锁
线程2 - 获取了锁
线程2 - 执行任务 0
线程2 - 执行任务 1
线程2 - 执行任务 2
线程2 - 执行任务 3
线程2 - 执行任务 4
线程2 - 释放了锁

解释:

  • 线程1 首先获取到锁并执行任务,在它释放锁之后,线程2 才可以获取到锁并执行任务。
  • 通过 ReentrantLock 的锁机制,两个线程不会同时执行任务,避免了线程之间的竞态条件。

6. 使用场景

ReentrantLock 适用于以下场景:

  1. 高并发情况下的资源控制:当多个线程需要访问共享资源时,可以使用 ReentrantLock 来控制线程对资源的访问顺序,避免数据不一致或线程安全问题。

  2. 复杂的同步需求

    • 需要精确控制锁的获取与释放。
    • 需要支持公平性(公平锁)。
    • 需要超时锁或可中断锁。
  3. 线程间协作:可以通过锁机制控制多个线程的执行顺序,避免不必要的资源竞争,提升程序执行效率。


7. ReentrantLock解决的问题

  1. 避免线程间的竞态条件:当多个线程同时访问共享资源时,未加锁的情况下可能会出现数据不一致或逻辑错误。ReentrantLock通过显式加锁,确保线程安全。

  2. 可重入性问题:传统锁可能会因为线程自身再次请求同一资源而导致死锁问题,而ReentrantLock通过可重入性设计避免了这种情况。

  3. 更高的灵活性和控制权:相比于 synchronized 关键字,ReentrantLock 提供了更强的灵活性,如公平性设置、可中断锁、超时锁等功能。


8. 业务场景中的应用

借助ReentrantLock,可以设计一个库存管理系统

  • 业务场景:在电商平台上,当用户进行下单时,需要扣减商品库存。为了保证并发环境下的库存正确性,多个并发的扣减操作必须确保同一时刻只能有一个线程操作库存。

  • ReentrantLock的应用:通过ReentrantLock对库存操作加锁,确保多个并发的库存操作互斥执行,防止超卖或库存不正确的问题。同时可以设置公平锁,确保用户的下单请求按先后顺序处理,提升用户体验。


9. 总结

ReentrantLock 提供了一种灵活且功能强大的线程同步机制,尤其适用于复杂的并发场景。相比于 synchronizedReentrantLock 允许更细粒度的控制,并支持公平锁、可中断锁和可重入锁等高级特性。在业务应用中,ReentrantLock 可以确保高并发情况下共享资源的正确性,适用于电商、库存管理等系统。


http://www.mrgr.cn/news/55634.html

相关文章:

  • 【LLM】大模型工具调用之AllTools模型
  • 【状态机DP】力扣1262. 可被三整除的最大和
  • 01-编程入门
  • 传感器信号的存储和传输
  • 首个统一生成和判别任务的条件生成模型框架BiGR:专注于增强生成和表示能力,可执行视觉生成、辨别、编辑等任务
  • Qt学习笔记第21到30讲
  • DataWhale10月动手实践——Bot应用开发task04学习笔记
  • MySQL 服务器配置与管理<二>
  • CAS 详解
  • Reverse.Kr—— 前四题
  • 08-流程控制语句
  • 简单汇编教程9 字符串与字符串指令
  • tkintrt.Button位置试炼——计算器“键盘”
  • MySQL—CRUD—进阶—(二) (ಥ_ಥ)
  • 基于springboot的网上服装商城推荐系统的设计与实现
  • BitNet: Scaling 1-bit Transformers for Large Language Models
  • 数据库中常用的函数及函数应用
  • FCITX5的一些小命令
  • Spring Boot:如何实现JAR包的直接运行
  • 静态代码块为什么不能放在构造函数中