可重入锁表示如果你已经获取了锁,如果你想再次获取该锁,依然能够正常获取到,不会因为之前已经获取过还没释放而阻塞。synchronized关键字是隐式的可重入锁,表现在synchronized修饰的同步方法内递归调用自己不会阻塞。jdk中实现Lock接口的可重入锁ReentrantLock
是如何实现可重入的呢?在阅读这篇文章之前,我假设你已经读过Lock内的同步器,对锁的实现有一定的了解。
可重入锁实现要点
- 获取锁后,再次获取该锁时;判断当前线程是否是已经获取锁的线程,若是,则通过;否则,阻塞。
- 释放锁时,如果该线程获取了n(n>=1)次锁,则需要释放n次,才能释放成功,返回true;否则,返回false。
- 获取可重入锁(非公平锁)同步状态的局部代码如下:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {//此时,锁未被获取
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//当前线程就是此时占有锁的线程
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
【可重入锁】代码中发现,如果当前线程就是已经占有锁的线程,则同步状态值进行增加,并返回true。
- 释放可重入锁(非公平锁)的局部代码如下:
protected final boolean tryRelease(int releases) {
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;
//同步状态值为初始值0时,返回true;否则,返回false
}
代码中发现同步状态值0是锁是否成功释放的标志。
- 获取可重入锁(公平锁)的局部代码如下:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//当前节点是否有前驱节点
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
与非公平锁获取同步状态相比,唯一的区别是判断引用当前线程的当前节点是否有前驱节点,如果返回true,说明有更早的节点在等待获取锁,则当前节点需要等到前驱节点获取并释放锁后才能够继续获取锁。至于释放锁,与非公平锁并无区别。
参考
- 并发编程的艺术