Java之AQS源码解析

锁状态:

枚举

含义

0

当一个Node被初始化的时候的默认值

CANCELLED

为1,表示线程获取锁的请求已经取消了

CONDITION

为-2,表示节点在等待队列中,节点线程等待唤醒

PROPAGATE

为-3,当前线程处在SHARED情况下,该字段才会使用

SIGNAL

为-1,表示线程已经准备好了,就等资源释放了

ReentrantLock内部会根据是否是公平锁创建NonfairSync,FairSync,同时Sync继承AQS

首先,说明加锁,

NonfairSync:

final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

FairSync:

final void lock() { acquire(1); }

NonfairSync,FairSync加锁唯一区别就是NonfairSync在锁空闲时候可以直接抢占锁,而FairSync必须要排队

acquire(1)方法调用的是AQS实现,

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

tryAcquire(arg)是模板方法,有各个实现类自己实现,

NonfairSync:

protected final boolean tryAcquire(int acquires) { return nonfairTryAcquire(acquires); }

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

FairSync:

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;
}

先来说说获取锁:lock-》acquire

公平锁和非公平锁的tryAcquire方法唯一区别是!hasQueuedPredecessors(),因为公平锁要排队获取,所以只有当前线程位于CLH队列头的第二个节点(自己就是第一个)才能获取锁。

再来看方法:

public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }

对于CLH队列的操作都是有AQS封装,非CLH队列的操作有实现类实现。

再来看addWaiter(Node.EXCLUSIVE),将线程加入CLH队列中,

private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    enq(node);
    return node;
}

1,创建当前线程的Node

2,当前tail尾部节点不为空,既队列以有其他等待线程,无需初始化,直接将node加入到尾部,tail指向node

3,tail为空,执行enq(node);

private Node enq(final Node node) { for (;;) { Node t = tail; if (t == null) { // Must initialize if (compareAndSetHead(new Node())) tail = head; } else { node.prev = t; if (compareAndSetTail(t, node)) { t.next = node; return t; } } } }

这部分主要是循环cas初始化队列和加入队列尾部,防止多线程并发初始化,如果未初始化,创建一个空Node头,再在队列尾部插入。

再看acquireQueued,acquireQueued会把放入队列中的线程不断去获取锁,直到获取成功或者不再需要获取既中断。

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);
    }
}

1,获取当前节点的前置node,如果前置node是head,同时锁空闲并获取到,那么将前置节点删除,并置空自己的node,设置成head

2,如果获取锁失败,就执行线程阻塞操作,如果阻塞中,线程被唤醒,要么就是正常唤醒则再次循环获取锁,否则就是中断唤醒,这个时候会执行cancelAcquire(node);将node状态设置成取消状态。

看shouldParkAfterFailedAcquire(p, node),判断是否获取锁失败应该阻塞:

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    if (ws == Node.SIGNAL)
        /*
         * This node has already set status asking a release
         * to signal it, so it can safely park.
         */
        return true;
    if (ws > 0) {
        /*
         * Predecessor was cancelled. Skip over predecessors and
         * indicate retry.
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
        /*
         * waitStatus must be 0 or PROPAGATE.  Indicate that we
         * need a signal, but don't park yet.  Caller will need to
         * retry to make sure it cannot acquire before parking.
         */
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

该方法主要是:node要阻塞自己,前提要将其前置节点状态置为-1(Node.SIGNAL),同时要置状态为-1的node状态必须是非取消状态,因为取消状态的节点后续会删除

parkAndCheckInterrupt:

private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }

阻塞线程,返回线程当前状态,同时重置线程状态。

如果出现异常或者出现中断,就会执行finally的取消线程的请求操作:

private void cancelAcquire(Node node) {
    // Ignore if node doesn't exist
    if (node == null)
        return;

    node.thread = null;

    // Skip cancelled predecessors
    Node pred = node.prev;
    while (pred.waitStatus > 0)
        node.prev = pred = pred.prev;

    // predNext is the apparent node to unsplice. CASes below will
    // fail if not, in which case, we lost race vs another cancel
    // or signal, so no further action is necessary.
    Node predNext = pred.next;

    // Can use unconditional write instead of CAS here.
    // After this atomic step, other Nodes can skip past us.
    // Before, we are free of interference from other threads.
    node.waitStatus = Node.CANCELLED;

    // If we are the tail, remove ourselves.
    if (node == tail && compareAndSetTail(node, pred)) {
        compareAndSetNext(pred, predNext, null);
    } else {
        // If successor needs signal, try to set pred's next-link
        // so it will get one. Otherwise wake it up to propagate.
        int ws;
        if (pred != head &&
            ((ws = pred.waitStatus) == Node.SIGNAL ||
             (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
            pred.thread != null) {
            Node next = node.next;
            if (next != null && next.waitStatus <= 0)
                compareAndSetNext(pred, predNext, next);
        } else {
            unparkSuccessor(node);
        }

        node.next = node; // help GC
    }
}

1,该方法会先删除node前置节点状态为Node.CANCELLED

2,如果自己是尾节点,直接删除自己

3,如果自己不是头节点,直接删除自己

4,如果前置节点就是head,调用unparkSuccessor(node);

private void unparkSuccessor(Node node) {
    /*
     * If status is negative (i.e., possibly needing signal) try
     * to clear in anticipation of signalling.  It is OK if this
     * fails or if status is changed by waiting thread.
     */
    int ws = node.waitStatus;
    if (ws < 0)
        compareAndSetWaitStatus(node, ws, 0);

    /*
     * Thread to unpark is held in successor, which is normally
     * just the next node.  But if cancelled or apparently null,
     * traverse backwards from tail to find the actual
     * non-cancelled successor.
     */
    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,如果自身waitStatus小于0,置为0

2,从后向前,找到最前面的t.waitStatus

tryAcquire直接获取锁失败,通过acquireQueued队列唤醒获取锁成功执行,将自己的线程状态重置:

selfInterrupt();

获取锁基本说完,再来说释放锁:unlock-》release

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

tryRelease:首先减去重入次数,如果当前释放锁线程不是获取锁的线程报错,如果重入次数为0,则重置锁,初始化状态和独占锁标识

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

if (h != null && h.waitStatus != 0)重点:

h == null Head还没初始化。初始情况下,head == null,第一个节点入队,Head会被初始化一个虚拟节点。所以说,这里如果还没来得及入队,就会出现head == null 的情况。这种只要自己释放就好,无需唤醒后继节点

h != null && waitStatus == 0 表明h节点是取消状态,后继节点对应的线程仍在运行中,不需要唤醒。

h != null && waitStatus < 0 表明h节点是肯能-1状态,后继节点可能被阻塞了,需要唤醒。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值