女篮世界杯分组

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

试想一下。如果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;

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

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