Reentrantlock
2020-08-31 本文已影响0人
ppamos
Reentrantlock是jdk提供的可重入锁的实现,它分为非公平锁和公平锁。公平锁是那个线程等待锁时间最长,那个就获得锁。非公平锁是那个抢到锁,锁就归哪一个。
Reentrantlock.lock()非公平锁源码解析。
final void lock() {
if (compareAndSetState(0, 1))
//尝试获取锁,如果获取成功,则设当前线程为已获取锁的线程。
setExclusiveOwnerThread(Thread.currentThread());
else
//重入锁的实现以及把争抢锁的线程放入队列中
acquire(1);
}
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()) {
//如果当前线程与获得锁的线程是同一个,则他为重入锁。
//重入次数加 1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
如果抢占不了锁权限,则把当前线程放入AQS同步队列中
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
//新建一个AQS队列
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
//第一次进入新建一个头结点
if (compareAndSetHead(new Node()))
//头结点和尾结点设置为同一结点
tail = head;
} else {
node.prev = t;
//把当前线程设为尾部结点,加入到AQS队列中
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
加入aqs队列示意图.jpg
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//如果当前结点的上个结点为头结点
//尝试争抢锁
//争抢锁成功,把当前结点设为头结点,去掉之前的头结点
//等待gc回收之前头结点
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
//设置标志位为-1,并且中断当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);//中断线程
return Thread.interrupted();
}
线程获取不到锁终端示意图.jpg
public final boolean release(int arg) {
//把锁次数减1,如果锁次数为0,则释放当前锁
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
//唤醒头结点的下一个结点,如果它存在的话
unparkSuccessor(h);
return true;
}
return false;
}
线程唤醒后,会从中断的地方开始执行,调用acquireQueued()方法里面的tryAcquire(arg),尝试重新获取锁。