首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >ReentrantLock(可重入锁)的应用、核心原理及实战

ReentrantLock(可重入锁)的应用、核心原理及实战

原创
作者头像
小草飞上天
发布2025-02-01 15:01:52
发布2025-02-01 15:01:52
9220
举报
文章被收录于专栏:java学习java学习

前言

在多线程编程中,一个线程可能会在持有某个锁的情况下,再次尝试获取同一个锁。如果锁不可重入,那这种嵌套锁的场景会导致线程死锁。

试想一下。如果A线程来调用一个递归函数,函数是需要锁的,当A线程获取锁后,再次递归调用方法时,发现已经有线程获取锁了,并且这个线程就是A线程自己。这种情况下应该怎么办?

所以,在JAVA中,为了解决这种问题,增加了可重入锁,可重入锁的的核心作用就是防止死锁。

什么是可重入锁?

可重入锁(ReentrantLock)是指应用中同一个线程在多层函数都有锁的情况下,如果在外层函数获得锁 ,那么对内层函数的调用 仍然能获取该锁的代码。也就是说同一个线程在外层方法获取锁的时候,在进入内层方法会自动获取锁。

简单点说,一个线程拥有了开门的钥匙后,那他就可以一直进入这个家里了。

需要注意的是。可重入锁必须确保锁在finally中释放,以避免因异常导致锁未释放,从而引发死锁。

我们看一下下面的代码:

在下面的测试代码中,如果 lock 是不可重入的,当 outerMethod 调用innerMethod 时,innerMethod 会尝试再次获取锁,但此时锁已经被 outerMethod 持有,导致线程进入死锁状态。

代码语言:txt
复制
package com.demo;

import java.util.concurrent.locks.ReentrantLock;

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

    public void outerMethod() {
        lock.lock();
        try {
            System.out.println("Outer method is running.");
            innerMethod();
        } finally {
            lock.unlock();
        }
    }

    public void innerMethod() {
        lock.lock();
        try {
            System.out.println("Inner method is running.");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        T7 demo = new T7();

        // 同一个线程调用 outerMethod 和 innerMethod
        demo.outerMethod();
    }
}

打印结果:

由打印结果可以直观的确定可重入锁的意义。

可重入锁原理

当线程尝试获取锁时,如果锁已被占用,则判断占用锁的是否是当前线程,

如果是当前线程执有锁,则可以再次获取锁,通过维护一个线程独有的锁计数器来实现。每次获取锁时计数器加1,释放锁时减1,只有当计数器为0时,锁才真正释放。

如果不是当前线程执有锁,则线程会被加入等待队列并阻塞;当锁释放时,队列中的线程被唤醒尝试获取锁。

代码语言:txt
复制
final boolean nonfairTryAcquire(int acquires) {
    // 获取当前线程
    final Thread current = Thread.currentThread();
    // 获取当前锁的状态(state),state 表示当前锁的持有次数
    int c = getState();
    
    // 如果当前锁的状态为 0,表示锁未被任何线程持有
    if (c == 0) {
        // 使用 CAS 操作尝试将锁的状态从 0 设置为 acquires(通常是 1)
        if (compareAndSetState(0, acquires)) {
            // 如果 CAS 成功,设置当前线程为锁的持有者
            setExclusiveOwnerThread(current);
            // 返回 true 表示获取锁成功
            return true;
        }
    }
    // 如果当前锁已被持有,并且持有者是当前线程(可重入锁的特性)
    else if (current == getExclusiveOwnerThread()) {
        // 计算新的锁状态值(当前状态 + acquires)
        int nextc = c + acquires;
        // 检查是否发生溢出(锁的最大持有次数超过 Integer.MAX_VALUE)
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        // 更新锁的状态为新的值
        setState(nextc);
        // 返回 true 表示获取锁成功
        return true;
    }
    // 如果当前线程无法获取锁(锁被其他线程持有,且当前线程不是持有者)
    return false;
}

可重试锁从最开始获取锁到时最后获取到锁的原理图如下:

总结

可重入锁的出现主要是为了解决嵌套锁导致的死锁问题。可重入锁这种解决死锁的方式,使得我们能够在复杂的并发编程中更加安全和高效地使用锁,而不用考虑其他问题。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 什么是可重入锁?
  • 可重入锁原理
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档