ReentrantLock
的实现是基于其内部类FairSync
(公平锁)和NonFairSync
(非公平锁)实现的。
可重入性是基于Thread.currentThread()
实现的: 如果当前线程已经获得了执行序列中的锁, 那执行序列之后的所有方法都可以获得这个锁。
公平锁和非公平锁获取方式大同小异
可重入性的实现基于下面代码片段的 else if
语句
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
...
// 尝试获取锁成功
}
else if (current == getExclusiveOwnerThread()) {
// 是当前线程,直接获取到锁。实现可重入性。
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
非公平锁NonfairSync
1.调用lock()方法时,首先去通过CAS尝试设置锁资源的state变量,如果设置成功,则设置当前持有锁资源的线程为当前请求线程
2.调用tryAcquire方法时,首先获取当前锁资源的state变量,如果为0,则通过CAS去尝试设置state,如果设置成功,则设置当前持有锁资源的线程为当前请求线程
以上两步都属于插队现象,可以提高系统吞吐量
非公平锁在实现的时候多次强调随机抢占:
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
与公平锁的区别在于新晋获取锁的进程会有多次机会去抢占锁。如果被加入了等待队列后则跟公平锁没有区别
公平锁FairSync
1.调用lock()方法时,不进行CAS尝试
2.调用tryAcuqire方法时,首先获取当前锁资源的state变量,如果为0,则判断该节点是否是头节点可以去获取锁资源,如果可以才通过CAS去尝试设置state
上面通过判断该线程是否是队列的头结点,从而保证公平性
公平锁的实现机理在于每次有线程来抢占锁的时候,都会检查一遍有没有等待队列,如果有, 当前线程会执行如下步骤:
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
其中hasQueuedPredecessors
是用于检查是否有等待队列的
ReentrantLock锁的释放
ReentrantLock锁的释放是逐级释放的,也就是说在 可重入性 场景中,必须要等到场景内所有的加锁的方法都释放了锁, 当前线程持有的锁才会被释放!
释放的方式很简单, state字段减一即可:
protected final boolean tryRelease(int releases) {
// releases = 1
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}