AQS是否会死锁呢?

376 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第2天,点击查看活动详情

独占锁模式 前提: t1线程获取到锁,t2线程未获取锁;

image.png

t2线程执行到指定代码
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);
    }
}


```
private final boolean parkAndCheckInterrupt() {
 // t2线程执行到此处   LockSupport.park(this);
    return Thread.interrupted();
}
```

t2线程在执行LockSupport.park(this)时发生上下文切换(该方法还未执行);

t1线程释放锁,准备唤醒t2
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor方法;
            (h);
        return true;
    }
    return false;
}

由于head节点的waitStatus=-1满足唤醒条件;进入unparkSuccessor方法;

private void unparkSuccessor(Node node) {
  
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

  
    Node s = node.next;
    if (s == null || s.waitStatus > 0) {
        s = null;
        for (Node t = tail; t != null && t != node; t = t.prev)
            if (t.waitStatus <= 0)
                s = t;
    }
    if (s != null)
        LockSupport.unpark(s.thread);
}
  1. t1线程首先将head节点的waitStatus通过cas设置为0;
  2. 从尾结点向前查找最后一个未取消的节点t2节点,将其唤醒;
  3. 但是t2线程并未执行LockSupport.park方法,此时无效唤醒;
  4. t2线程切换回来,开始执行LockSupport.park()方法,此时t2线程永远无法被唤醒,造成死锁;

意想不到

你以为上述就结束了,其实并没有,当我去翻看LockSupport.park的源码时发现:

LockSupport#park

LockSupport提供的是一个许可,如果存在许可,线程在调用park的时候,会立马返回,此时许可也会被消费掉,如果没有许可,则会阻塞;

LockSupport#unpark

为线程提供“许可(permit)”,线程调用park函数则等待“许可”。这个有点像信号量,但是这个“许可”是不能叠加的,“许可”是一次性的;

总结

上述的猜想前提: 获取锁的线程先unpark,未获取锁的线程再park时发生死锁;再结合源码分析这种情况不成立,所以不会发生死锁;