- interrupt() 线程标记为中断,抛异常.
- isInterrupt() 判断线程是否中断,并且重置为false.
-
为什么需要虚拟头结点(来自路神笔记)
tf:第一个给lock加锁的线程
如果是第一个线程tf,那么和队列无关,线程直接持有锁。并且也不会初始化队列,如果接下来的线程都是交替执行,那么永远和AQS队列无关,都是直接线程持有锁,如果发生了竞争,比如tf持有锁的过程中T2来lock,那么这个时候就会初始化AQS,初始化AQS的时候会在队列的头部虚拟一个Thread为NULL的Node,因为队列当中的head永远是持有锁的那个node(除了第一次会虚拟一个,其他时候都是持有锁的那个线程锁封装的node),现在第一次的时候持有锁的是tf而tf不在队列当中所以虚拟了一个node节点,队列当中的除了head之外的所有的node都在park,当tf释放锁之后unpark某个(基本是队列当中的第二个,为什么是第二个呢?前面说过head永远是持有锁的那个node,当有时候也不会是第二个,比如第二个被cancel之后,至于为什么会被cancel,不在我们讨论范围之内,cancel的条件很苛刻,基本不会发生)node之后,node被唤醒,假设node是t2,那么这个时候会首先把t2变成head(sethead),在sethead方法里面会把t2代表的node设置为head,并且把node的Thread设置为null,为什么需要设置null?其实原因很简单,现在t2已经拿到锁了,node就不要排队了,那么node对Thread的引用就没有意义了。所以队列的head里面的Thread永远为null。公平锁和非公平锁区别
公平锁加锁流程
AQS 结构为 headNode:头节点,tailNode:尾结点,state:加锁次数,exclusiveOwnerThread:当前获取锁的线程;
node结构:pre,next,waitState:节点等待状态(主要类型 -1:后面有节点等待状态,0:正常等待状态,1:取消获取锁 ),node当前线程。 lock 调用 acquire()方法,AQS 的 acquire()分3个方法 tryAcquire()尝试加锁,aqurieQueued()加锁失败,进入队列,等待队列被唤醒,selfInterrupt()重置interrupt()作用不大。
- tryAcquire主要作用尝试加锁。返回 true 加锁成功,返回 false 加锁失败。
- 首先getState()判断是否为 0 。(有两种可能为0,一种是锁长时间为自由状态,一种是锁刚刚被释放 短暂的自由状态)
- 如果为 0 ,判断 是否有 headNode ,再判断是否 有其他的 head 后面是否有其他线程在排队。
- 如果没有队列 或者 队列中没有其他线程在排队,直接 CAS 尝试加锁,加锁成功返回 true, 加锁失败 返回 false。
- 加锁成功将AQS 中的 获取锁的线程 设置为 当前 node 线程。
- 再判断 持有锁的线程是否是当前线程,也就是判断是否是 重入锁。
- 如果是重入锁, state + 1,返回 true。
- 否则其他情况返回 false 。
- 加锁失败首先会执行 addWaiter 方法,主要作用是将当前 线程封装成 node 插入 tail 和 是否需要初始化队列。
- 先将当前线程,封装成 node。
- 判断 head 是否为 null
- 如果不为 null,说明已经存在队列,设置当前节点 pre 为 tail,当前节点设置为 tail。
- 如果为 null,说明不存在队列,去初始化队列。
- 首先死循环,判断 tail 是否等于 null ,等于 null 通过 CAS 去初始化为 空节点,赋值给 head,同时tail = head。
- tail 不为 null,设置 node pre 为 tail,然后通过CAS 设置 node 为 tail,如果将当前节点 设置为 tail 节点成功。然后跳出循环。(一般是第二次循环会进入这里)
- 返回 当前 封装的 head。
- 然后会执行 acquireQueued(Node node, int arg)将队列中 node 等待唤醒和尝试加锁。
- 死循环,判断当前节点 pre 是否是 head, 也就是为了判断当前节点是否为 队列中第二个等待节点。
- 如果是 head ,当前 node 线程 CAS 去尝试获取锁。
- 获取成功, 将 当前 node 的 pre 设置为 null,和线程设置为 null。 把 head 的 next 设置 null ,相当于把 head 断开连接,方便gc回收。当前节点设置为 头节点。
- 获取失败,则进入 下面的步骤。
- 如果获取锁失败 或者 pre 不是 head 节点
- 首先判断上一个节点的 waitState 是否为 -1,不是 -1 设置为 -1。
- 然后再次循环到这里,会将自己 park住。等待被唤醒。
- 如果被其他线程唤醒,则先进行 Thread.interrupted(),这里没什么作用,主要是 lockInterruptibly() 会抛异常。
- 被唤醒之后,就会再次进入循环 尝试获取锁。
- 出现异常,会走 finally 取消线程获取锁。(一般来讲不会走这里)
- 当 Thread.interrupted()为 true,则会调用 selfInterrupt(),重置当前线程 interrupt。
// tryAcquire() 尝试获取锁
// acquireQueued() 入队
// selfInterrupt 消除方法中的 interrupt();
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// lock()方法,等待锁和尝试获取锁逻辑
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这里的interrupted()或判断是否中断,中断返回true/*
* Acquires in exclusive interruptible mode.
* @param arg the acquire argument
*/
// lockInterruptibly()方法,等待锁和尝试获取锁逻辑
private void doAcquireInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
lock()的parkAndCheckInterrupt(),返回的是true,那么acquireQueue就返回true
,那么就走selfInterrupt(),那么selfInterrupt()那个中断重置了。
但是在lockInterruptibly(),parkAndCheckInterrupt()会抛弃异常,我们写的方法中就可以捕获该异常。
结合两者看 selfInterrupt()就有意义了。 ```java // Convenience method to park and then check if interrupted. // @return {@code true} if interrupted
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
```java
// 尝试获取锁的逻辑
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;
}
/**
* Convenience method to interrupt current thread.
*/
// 方便的方法来中断当前线程,重置。
static void selfInterrupt() {
Thread.currentThread().interrupt();
}