ReentrantLock源码分析
ReentrantLock
是 Java 并发库中的一个重要类,它提供了比 synchronized
更灵活的锁机制。ReentrantLock
是一个互斥锁,具有与使用 synchronized
方法和语句所访问的隐式监视器锁类似的语义,但功能更强大。
功能结构
ReentrantLock
基于 AQS(AbstractQueuedSynchronizer)框架,通过继承 AQS 并实现其方法来管理锁的状态。有以下核心点:
- 状态管理:
ReentrantLock
使用一个整型变量来表示锁的状态,0 表示锁未被任何线程持有,大于 0 表示锁被某个线程持有,并且数值表示重入的次数。 - 锁的获取:当线程调用
lock()
方法时,会尝试通过 CAS(Compare-And-Swap)操作将锁状态从 0 改为 1。如果成功,表示该线程获取到了锁,并将当前线程设置为锁的拥有者。如果失败,表示锁已经被其他线程持有,当前线程会进入自旋等待或阻塞状态。 - 锁的释放:当线程调用
unlock()
方法时,会释放锁,即将锁状态减 1。如果状态变为 0,表示没有线程持有锁,此时会唤醒等待队列中的一个线程(如果是公平锁,则唤醒等待最久的线程;如果是非公平锁,则可能唤醒任意一个等待的线程)。 - 重入性:由于
ReentrantLock
支持重入,即同一个线程可以多次获取锁而不会被阻塞。这是通过维护一个计数器来实现的,每次线程获取锁时计数器加 1,每次释放锁时计数器减 1。只有当计数器变为 0 时,才表示该线程完全释放了锁。 - 公平性:
ReentrantLock
提供了公平锁和非公平锁两种实现。公平锁会严格按照线程请求锁的顺序来分配锁,而非公平锁则不会。这主要影响线程获取锁的策略和性能。 - LockSupport:
ReentrantLock
使用LockSupport
的park()
和unpark()
方法来阻塞和唤醒线程。当一个线程无法获取锁时,它会被park()阻塞;当锁被释放时,通过unpark()来唤醒等待的线程。
ReentrantLock 的状态管理主要是通过内部类 Sync 实现的,这个内部类继承自 AbstractQueuedSynchronizer(AQS)。AQS 提供了一个整型的 state 字段来表示同步状态,ReentrantLock 利用这个字段来表示锁是否被持有以及重入的次数。
ReentrantLock 内部的 Sync 类继承自 AbstractQueuedSynchronizer(AQS)。Sync 类提供了锁的基础实现,并定义了几个核心方法来控制锁的获取和释放。
Sync 类中的关键方法:
-
tryAcquire(int acquires):
- 作用:尝试获取锁。
- 参数
acquires
通常表示尝试获取的锁的数量,对于ReentrantLock
而言,这个值通常是1。 - 如果成功获取锁,则返回
true
;否则,返回false
。 - 这个方法在AQS中是一个需要由子类实现的方法,
Sync
类提供了具体的实现逻辑。
-
tryRelease(int releases):
- 作用:尝试释放锁。
- 参数
releases
表示要释放的锁的数量,对于ReentrantLock
而言,这个值通常是1。 - 如果成功释放锁,并且锁的状态变为0(即完全释放),则返回
true
;否则,返回false
。 - 这个方法同样需要在AQS的子类中实现,
Sync
类提供了具体逻辑。
-
isLocked():
- 作用:判断锁是否被某个线程持有。
- 返回
true
如果锁被持有,否则返回false
。
-
isHeldExclusively():
- 作用:判断当前线程是否独占持有锁。
- 如果当前线程是锁的独占拥有者,则返回
true
;否则,返回false
。
除了这些方法,Sync
类还可能需要实现AQS中的其他一些方法,如 tryAcquireShared
和 tryReleaseShared
,但对于 ReentrantLock
这种独占锁来说,这些方法通常不需要实现,因为 ReentrantLock
不支持共享锁模式。
Sync
类还有两个子类:NonfairSync
和 FairSync
,分别对应非公平锁和公平锁的实现。这两个子类主要重写了 tryAcquire
方法以实现不同的锁获取策略。
-
NonfairSync: 在非公平锁中,
tryAcquire
方法允许插队,即当前线程可以不管等待队列中的其他线程而直接尝试获取锁。 -
FairSync: 在公平锁中,
tryAcquire
方法会检查等待队列中是否有其他线程在等待,如果有,则当前线程会排队等待,遵循FIFO(先进先出)原则。
总的来说,Sync
类及其子类提供了 ReentrantLock
的核心同步机制,通过实现AQS中定义的方法来控制锁的获取、释放和状态管理。
ReentrantLock核心方法
构造方法
|
|
这里定义了公平锁和非公平锁的实现。公平锁会按照线程请求锁的顺序来分配锁,而非公平锁则不会。
lock() 方法(以非公平锁为例)
|
|
这个方法尝试获取锁,如果当前没有线程持有锁,则当前线程会获取到锁并成为锁的拥有者。如果锁已经被其他线程持有,则当前线程会尝试自旋等待或进入阻塞状态。
unlock() 方法
|
|
这个方法用于释放锁,使得其他等待的线程有机会获取到锁。
Sync TryAcquire
tryAcquire
方法尝试获取锁。以下是 NonfairSync
(非公平锁)的实现示例,因为非公平锁和公平锁的主要区别在于获取锁的策略,而释放锁的逻辑是相同的。
|
|
Sync TryRelease
tryRelease
方法尝试释放锁,并减少锁的重入计数。这个方法在公平锁和非公平锁中的实现是相同的。
|
|
IsLocked 和 IsHeldExclusively
isLocked
和 isHeldExclusively
方法用于检查锁的状态和拥有者。
|
|
这些核心方法通过操作AQS的内部状态(state
)和独占线程(exclusiveOwnerThread
)来管理锁。state
是一个整数,表示锁被持有的次数(对于重入锁,它表示重入的次数)。exclusiveOwnerThread
是一个 Thread
对象,表示当前持有锁的线程。
注意,这里的源码只是简化和注释版本,用于说明核心方法的实现原理。实际的 ReentrantLock
源码包含更多的错误处理、性能优化和同步机制。如果需要查看完整的实现,请查阅Java标准库中的源代码。