1.锁竞争后的操作-阻塞
开始阻塞
在开始阻塞之前,在尝试获取锁一次
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
如果是第一个节点head 再次进行获取加锁的权限 原因是由内核态到用户态的切换很消耗性能,而AQS要尽可能的避免这种切换 注意:再次在此过程中会丢弃最初的head节点也就是买那个空节点,在竞争之后,会把刚加入的那个节点再一次初始化,作为队列的头节点。
判断是否能阻塞
第一轮循环:
通过首节点的状态判断下一个节点是否能够被唤醒,将head的节点状态更改为-1
第二轮循环:
head的状态已经被改变,可以之后的节点可以被阻塞了,并且判断阻塞是否是由中断信号引起的
开始释放锁
开始释放锁,head节点的状态由0 --> -1 线程已经被唤醒去竞争加锁,对于公平锁,直接去获取锁没有问题,head节点会被直接抛弃,当前的node节点会成为新的head的节点;但是对于非公平锁而言,线程被唤醒的同时会有外面进来的线程同时竞争加锁的权限,若成功,和公平锁一致;若是失败,线程将再次被阻塞,注意,此次阻塞不会把线程添加到阻塞队列的队尾,而是通过修改node节点的前驱节点的状态,再一次将其阻塞,即head的节点状态再次有-1–>0
唤醒-中断和release
中断唤醒:
中断后会改变指向该线程的节点的状态,doAcquireInterruptibly(int arg)方法会把节点状态更改为cancel状态,在阻塞队列中剔除,执行中断后的逻辑。