解析可重入锁的核心就是理解同步器 1.同步器(aqs):什么是同步器、同步器是一种数据结构、是同步队列的头节点、同步器中提供了大量的模板方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
以acquire方法为例子、同步器中并没有实现tryAcquire的方法、
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
具体的实现需要通过子类重写tryAcquire方法来实现、接下来介绍addWaiter、acquireQueued的方法
addWaiter解析:
private Node addWaiter(Node mode) {
Node node = new Node(mode);
for (;;) {
Node oldTail = tail;
if (oldTail != null) {
node.setPrevRelaxed(oldTail);
if (compareAndSetTail(oldTail, node)) {
oldTail.next = node;
return node;
}
} else {
initializeSyncQueue();
}
}
}
可以看出在当前线程tryAcquire尝试获取锁失败时、执行addWaiter的方法会通过当前线程构造同步节点 主要的方法compareAndSetTail会通过乐观锁的方式为构造出的node节点以尾插法插入同步队列中、之后返回设置好的节点加入acquireQueued中
final boolean acquireQueued(final Node node, int arg) {
boolean interrupted = false;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node))
interrupted |= parkAndCheckInterrupt();
}
} catch (Throwable t) {
cancelAcquire(node);
if (interrupted)
selfInterrupt();
throw t;
}
}
可以看出先会判断传入的节点是否拥有前驱节点、如果前驱节点与同步器中的节点也就是头节点相等、之后会去再次调用tryAcquire方法、尝试去加锁、如果加锁失败就是不断自旋、直到获取了锁 如果加锁成功、会设置头节点为传入的构造节点、并且抛出头节点进入gc环节(真是太残忍了、用完就扔掉)、返回布尔值 以上就是同步器的流程了 现在进入可重入锁的源码、之前我们说过acquire是模板方法、需要子类去实现tryAcquire方法、可以看出可重入锁是怎么实现的、由于可重入锁是默认的不公平锁、
public ReentrantLock() {
sync = new NonfairSync();
}
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
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;
}
由以上代码可以看出、tryAcquire是通过设置同步器的state也就是状态完成的、当c=0时会通过乐观锁去设置state的状态、0是无线程进入锁、设置好锁之后、会设置线程暂存区域、方便后续、重入锁的设置中的线程判断、防止其他线程获取锁、当同一线程获取锁之后、state也会加1、以上就完成了可重入锁的所有流程。
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名,转载请标明出处 最后编辑时间为: 2021/11/22 14:56