可重入锁,就是支持重入的锁,它表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁还支持获取公平锁和非公平锁。
在绝对的时间上,先对锁进行获取请求的一定先被满足,那么这个锁是公平锁,反之,是非公平锁。
公平锁往往没有非公平锁的效率高,但是,并不是任何场景都是以TPS作为唯一的指标,公平锁能够减少“饥饿”的概率。
实现重进入
重进入是指任意线程在获取到锁后能够再次获取该锁而不会被阻塞,这需要解决两个问题:
1)线程再次获取锁。锁需要识别获取锁的线程,如果是则再次获取成功
2)锁的最终释放。线程重复n次获取了锁,随后在第n次释放锁后,其他线程能够获取到该锁。锁的最终释放要求锁对于获取进行计数自增,计数表示当前锁被重复获取的次数,而锁释放时,计数自减,当计数等于0时表示锁已经成功释放。
公平与非公平获取锁的区别
公平性与否是针对获取锁而言的,如果一个锁是公平的,那么获取锁的顺序就应该符合请求的绝对顺序,也就是FIFO。
非公平锁的获取
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//cas获取锁
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;
}
公平锁的获取
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;
}
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());
}
锁的释放
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
//拥有锁的线程不是当前线程,报错
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
//计数为0时,释放锁,将拥有锁的线程设置为null
free = true;
setExclusiveOwnerThread(null);
}
//更新计数
setState(c);
return free;
}