ReentrantLock实现可重入性的关键在于其内部维护了一个同步状态(state)计数器,该计数器用于记录当前线程获取锁的次数。以下是ReentrantLock实现可重入性的详细解释:
一、同步状态计数器
ReentrantLock内部有一个名为sync的同步器对象,它继承自AbstractQueuedSynchronizer(AQS)。AQS是Java并发包中提供的一个用于构建锁和其他同步组件的框架。在AQS中,有一个名为state的字段,它是一个整数,用于表示同步状态。
在ReentrantLock中,state字段被用作计数器,用于记录当前线程获取锁的次数。当一个线程首次获取锁时,state被初始化为1,表示该线程已经获取了一次锁。如果同一个线程再次请求锁(即重入),state会递增,以记录该线程获取锁的次数。当线程释放锁时,state会递减,直到state降为0时,锁才被完全释放,此时其他线程才能获取该锁。
二、可重入性的实现原理
-
获取锁:
- 当一个线程尝试获取锁时,ReentrantLock会调用其内部的sync对象的nonfairTryAcquire或tryAcquire方法(取决于锁的类型,非公平锁或公平锁)。
- 在这些方法内部,会首先检查state是否为0,即锁是否未被任何线程持有。如果是,则尝试通过CAS操作将state设置为1,并将当前线程设置为锁的持有者。如果CAS操作成功,则表示该线程成功获取了锁。
- 如果state不为0,即锁已被其他线程持有,则会检查当前尝试获取锁的线程是否是当前持有锁的线程。如果是,说明发生了重入,此时会将state递增,并返回true表示获取锁成功。
-
释放锁:
- 当一个线程释放锁时,ReentrantLock会调用其内部的sync对象的tryRelease方法。
- 在该方法内部,会首先检查当前线程是否是锁的持有者。如果不是,则抛出异常。
- 如果是锁的持有者,则会将state递减,并检查state是否已降为0。如果是,表示锁已被完全释放,此时会将持有锁的线程设置为null,以便其他线程可以获取锁。
三、示例代码
以下是一个简单的Java代码示例,演示了ReentrantLock的可重入性:
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++;
System.out.println(Thread.currentThread().getName() + " - count: " + count);
increment(); // 递归调用,测试ReentrantLock的可重入性
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
// 创建并启动线程
Thread thread1 = new Thread(example::increment, "Thread-1");
thread1.start();
}
}
综上所述,ReentrantLock通过内部维护一个同步状态计数器(state)来实现可重入性。当一个线程多次获取锁时,计数器会递增;当线程释放锁时,计数器会递减。直到计数器降为0时,锁才被完全释放。这种机制使得同一个线程可以多次获取同一个锁而不会导致死锁。在这个示例中,我们创建了一个ReentrantLock对象来保护共享资源count,并在increment()方法中获取锁。然后,我们递归调用increment()方法来测试ReentrantLock的可重入性。由于ReentrantLock是可重入的,因此同一个线程可以多次获取同一个锁而不会导致死锁。运行程序后,你会看到线程Thread-1能够不断地递归调用increment()方法,并正确地增加count的值。
1474

被折叠的 条评论
为什么被折叠?



