AbstractQueuedSynchronizer基本概念

AQS

同步状态

private volatile int state;

//1
protected final int getState() {
    return state;
}
//2
protected final void setState(int newState) {
    state = newState;
}
//3
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

Node:

头节点的线程对象为空,头节点就是当前持有锁的线程

//Node的职责 
//【准备唤醒下一个SIGNAL  准备退出CANCELLED  条件队列CONDITION  PROPAGATE共享头节点】
volatile int waitStatus;

AQS 加锁 acquire

  1. 用户实现:获取锁成功或失败逻辑
  2. AQS实现:失败了构造节点,放入阻塞队列等待
public final void acquire(int arg) {
    //1. tryAcquire由用户自己定义,取锁是成功还是失败
	//2. 放入阻塞队列
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

注:Lock

在ReentrantLock的lock方法中,tryAcquire()的实现就是使用CAS的方式获取锁,如果tryAcquire获取失败,则在acquireQueued()中会调用底层的LockSupport.park(),也就是操作系统的线程阻塞方法,这就是锁升级的过程

1 addWiter:把线程构建为节点,加入同步队列 (头节点是当前持有锁的线程)

2.acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

  1. 加入同步队列,之前在挣扎一下,有可能前一个线程结束了,所以先tryAcquire一下

  2. 如果获取失败,那么阻塞等待

    1. shouldParkAfterFailedAcquire 又给了一次机会抢锁
    2. parkAndCheckInterrupt调用 LockSupport.park(this) 阻塞
final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
        boolean interrupted = false;
        for (;;) {
            final Node p = node.predecessor();
            //1. 获取锁之前必须前驱是头节点
            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);
    }
}

Lock如何实现不可响应中断

park()会响应中断,会使阻塞线程唤醒,不会抛出异常,唤醒就是重新acquireQueued()尝试是否能获取到锁

AQS 解锁 release

  1. 用户实现:获解锁成功或失败逻辑
  2. AQS实现:成功了唤醒后继节点(unparkSuccessor)
    1. 先找头节点下一个
    2. 找不到从tail往前找
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        
        //只有后面没有节点,刚刚有节点加入,才唤醒(通过waitStatus来判断)
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}