目录
公平锁实现原理:
它会判断你的AQS队列中是否有前驱节点,没有才会去竞争;
判断方法hasQueuedPredecessors():1.根节点head后是否有第二个节点 2.拥有者是否是当前线程;
条件变量-await
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter();
int savedState = fullyRelease(node);
int interruptMode = 0;
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
1.Node node = addConditionWaiter();
先进入addConditionWaiter()——>目的:将线程放入Condition队列中,将线程封装成一个Node节点(关联线程,加入条件等待队列);
private Node addConditionWaiter() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
t = lastWaiter;
}
//创建一个新的Node,关联线程,状态为-2
Node node = new Node(Node.CONDITION);
//如果Condition队列首节点firstWaiter为null,就将节点设置为头节点,若不为null,就设置为Condition队列中最后一个节点;
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
2.int savedState = fullyRelease(node);
目的->因为线程其实是拿到锁资源的,只是因为我加了条件变量,如果不满足就触发await方法;
既然拿了锁资源,还要将其阻塞,那么我们肯定是要将它的state重置;
而fullyRelease:
就是一次释放,令state为-1;
final int fullyRelease(Node node) {
try {
int savedState = getState();
//释放节点,混淆头节点的下一个节点
if (release(savedState))
return savedState;
throw new IllegalMonitorStateException();
} catch (Throwable t) {
node.waitStatus = Node.CANCELLED;
throw t;
}
}
release中的unparkSuccessor(Node node)会唤醒下一个节点;
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
条件变量之唤醒-signal
public final void signal() {
//先判断是不是锁的持有者
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//找到条件变量队列中的第一个元素
Node first = firstWaiter;
if (first != null)
//队首不为null调用doSignal
doSignal(first);
}
唤醒的话,我们可以知道doSignal的目的就是将条件变量队列中的节点断开,然后放到竞争队列中(等待队列中)
1.第一个firstWaiter往后走一个并指向null ,最后一个也指向null;
2.transferForSignal:将节点放到AQS队列尾部,节点的waitStatis改为0,它的前一个节点为-1;
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
transferForSignal:目的就是将Condition队列中的节点状态修改为0,然后加入等待队列中尾部,并且修改它的前驱节点状态,让前驱节点唤醒它,核心也就是unpark;
final boolean transferForSignal(Node node) {
//进行CAS操作,将当前节点waitStatus设置为0;
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
//将节点加入等待队列中,尾部,成功的话就会返回它的前驱节点
Node p = enq(node);
//检查当前节点前驱节点的状态:目的是将状态改为-1 (占位作用,去唤醒下一个元素,也就是我们这个节点)
int ws = p.waitStatus;
//修改前驱节点的状态
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
//底层还是unpark唤醒
LockSupport.unpark(node.thread);
return true;
}