参考答案:
ReentrantLock 是 Java 并发包中的一种显式锁实现,它提供了比 synchronized 更强大的功能,能够灵活地控制线程的加锁和释放锁。它是 java.util.concurrent.locks 包中的一部分,常用于实现线程同步控制,支持公平性和重入等特性。
ReentrantLock 是一种可重入的锁,即同一个线程在获得锁后,可以多次进入该锁而不发生死锁。这种特性使得 ReentrantLock 能够支持递归调用的场景。ReentrantLock 是 Lock 接口的实现,它的主要优点是:
ReentrantLock 的实现主要依赖于 java.util.concurrent.locks.AbstractQueuedSynchronizer(AQS)类。AQS 是一个提供了先进先出的同步队列的抽象类,它为实现各种同步器(如锁、信号量等)提供了通用的框架。
ReentrantLock 基于 AQS 来实现线程同步。它通过 AQS 提供的 acquire 和 release 方法来控制锁的获取和释放。
ReentrantLock 内部通过一个 int 类型的变量 state 来记录锁的状态。state 的值主要表示两个方面的信息:
锁是否被占用
锁的重入次数(即同一个线程获取锁的次数)
当 state 为 0 时,锁是空闲的。
当 state 大于 0 时,锁被占用,state 的值表示当前线程重入锁的次数。
lock() 方法)当调用 ReentrantLock 的 lock() 方法时,实际执行的是 AQS 的 acquire() 方法,流程如下:
state 等于 0,表示锁没有被占用,当前线程可以通过 compareAndSetState 操作将 state 设置为 1,即占用锁,并将当前线程设置为持有锁的线程。state 大于 0,表示锁已经被其他线程占用,当前线程会进入 AQS 的等待队列中,等待锁被释放。具体来说,线程会尝试通过 acquireQueued 方法进入队列等待,并在适当的时候重新尝试获取锁。ReentrantLock 会通过增加 state 的值来支持重入锁操作(即允许当前线程多次调用 lock() 方法)。1public void lock() { 2 if (Thread.currentThread() == getOwner()) { 3 state++; 4 return; 5 } 6 // 非公平锁的获取 7 if (!tryAcquire(state)) { 8 enqueue(Thread.currentThread()); 9 } 10}
unlock() 方法)当调用 ReentrantLock 的 unlock() 方法时,实际执行的是 AQS 的 release() 方法,流程如下:
state 减 1。state 为 0,表示该线程已完全释放了锁,它会调用 release 方法来通知其他线程并唤醒等待队列中的线程。state 大于 0,表示当前线程仍然持有锁,其他线程无法获取锁。1public void unlock() { 2 if (Thread.currentThread() != getOwner()) { 3 throw new IllegalMonitorStateException(); 4 } 5 state--; 6 if (state == 0) { 7 setOwner(null); 8 // 唤醒等待队列中的下一个线程 9 release(); 10 } 11}
ReentrantLock 提供了公平锁和非公平锁的选择:
在创建 ReentrantLock 时,通过传入一个布尔值来指定是否启用公平锁:
1ReentrantLock lock = new ReentrantLock(true); // 公平锁 2ReentrantLock lock = new ReentrantLock(false); // 非公平锁
AQS 是 ReentrantLock 内部实现的核心类。它通过一个同步队列来管理线程的等待和唤醒。每个线程在获取锁时,如果锁不可用,就会被挂起,并加入到 AQS 的队列中,等待被唤醒后重新尝试获取锁。
state 来记录当前的同步状态。在 ReentrantLock 中,state 表示锁的占用情况。AQS 的实现依赖于 acquire 和 release 方法来控制线程的加锁和释放。当线程释放锁时,如果有其他线程等待锁,则会唤醒它们,并重新争夺锁。
ReentrantLock 支持线程的重入性,允许同一线程多次获得锁,而不会发生死锁。ReentrantLock 支持响应中断,线程可以在等待锁时被中断,从而避免长时间阻塞。ReentrantLock 提供了 Condition 机制,可以更灵活地控制线程的同步。ReentrantLock 的实现比 synchronized 更复杂,使用时需要手动释放锁,否则会导致死锁。ReentrantLock 比 synchronized 提供了更多功能,但由于额外的复杂性,它的性能可能比 synchronized 略低。ReentrantLock 示例1public class ReentrantLockExample { 2 private final ReentrantLock lock = new ReentrantLock(); 3 4 public void criticalSection() { 5 lock.lock(); 6 try { 7 // 执行临界区代码 8 System.out.println("In critical section"); 9 } finally { 10 lock.unlock(); 11 } 12 } 13}
最近更新时间:2024-12-06