0%

# AQS篇一

Posted on 2021-02-14 In Java , 并发 Views: Views: Disqus:

很多东西都是时看时新，而且时间长了也会忘，所以再来复习下，也会有一些新的角度看法这次来聊下AQS的内容，主要是这几个点，

## 第一个线程

第一个线程抢到锁了，此时state跟阻塞队列是怎么样的，其实这里是之前没理解对的地方

    /**
             * Fair version of tryAcquire.  Don't grant access unless
             * recursive call or no waiters or is first.
             */
            protected final boolean tryAcquire(int acquires) {
                final Thread current = Thread.currentThread();
                int c = getState();
                // 这里如果state还是0说明锁还空着
                if (c == 0) {
                    // 因为是公平锁版本的，先去看下是否阻塞队列里有排着队的
                    if (!hasQueuedPredecessors() &&
                        compareAndSetState(0, acquires)) {
                        // 没有排队的，并且state使用cas设置成功的就标记当前占有锁的线程是我
                        setExclusiveOwnerThread(current);
                        // 然后其实就返回了，包括阻塞队列的head和tail节点和waitStatus都没有设置
                        return true;
                    }
                }
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0)
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
                // 这里就是第二个线程会返回false
                return false;
            }
        }

## 第二个线程

当第二个线程进来的时候应该是怎么样，结合代码来看

    /**
         * Acquires in exclusive mode, ignoring interrupts.  Implemented
         * by invoking at least once {@link #tryAcquire},
         * returning on success.  Otherwise the thread is queued, possibly
         * repeatedly blocking and unblocking, invoking {@link
         * #tryAcquire} until success.  This method can be used
         * to implement method {@link Lock#lock}.
         *
         * @param arg the acquire argument.  This value is conveyed to
         *        {@link #tryAcquire} but is otherwise uninterpreted and
         *        can represent anything you like.
         */
        public final void acquire(int arg) {
            // 前面第一种情况是tryAcquire直接成功了，这个if判断第一个条件就是false，就不往下执行了
            // 如果是第二个线程，第一个条件获取锁不成功，条件判断!tryAcquire(arg) == true，就会走
            // acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
                selfInterrupt();
        }

然后来看下addWaiter的逻辑

    /**
         * Creates and enqueues node for current thread and given mode.
         *
         * @param mode Node.EXCLUSIVE for exclusive, Node.SHARED for shared
         * @return the new node
         */
        private Node addWaiter(Node mode) {
            // 这里是包装成一个node
            Node node = new Node(Thread.currentThread(), mode);
            // Try the fast path of enq; backup to full enq on failure
            // 最快的方式就是把当前线程的节点放在阻塞队列的最后
            Node pred = tail;
            // 只有当tail，也就是pred不为空的时候可以直接接上
            if (pred != null) {
                node.prev = pred;
                // 如果这里cas成功了，就直接接上返回了
                if (compareAndSetTail(pred, node)) {
                    pred.next = node;
                    return node;
                }
            }
            // 不然就会继续走到这里
            enq(node);
            return node;
        }

然后就是enq的逻辑了

    /**
         * Inserts node into queue, initializing if necessary. See picture above.
         * @param node the node to insert
         * @return node's predecessor
         */
        private Node enq(final Node node) {
            for (;;) {
                // 如果状态没变化的话，tail这时还是null的
                Node t = tail;
                if (t == null) { // Must initialize
                    // 这里就会初始化头结点，就是个空节点
                    if (compareAndSetHead(new Node()))
                        // tail也赋值成head
                        tail = head;
                } else {
                    // 这里就设置tail了
                    node.prev = t;
                    if (compareAndSetTail(t, node)) {
                        t.next = node;
                        return t;
                    }
                }
            }
        }

所以从这里可以看出来，其实head头结点不是个真实的带有线程的节点，并且不是在第一个线程进来的时候设置的

## 解锁

通过代码来看下

    /**
         * Attempts to release this lock.
         *
         * <p>If the current thread is the holder of this lock then the hold
         * count is decremented.  If the hold count is now zero then the lock
         * is released.  If the current thread is not the holder of this
         * lock then {@link IllegalMonitorStateException} is thrown.
         *
         * @throws IllegalMonitorStateException if the current thread does not
         *         hold this lock
         */
        public void unlock() {
            // 释放锁
            sync.release(1);
        }
    /**
         * Releases in exclusive mode.  Implemented by unblocking one or
         * more threads if {@link #tryRelease} returns true.
         * This method can be used to implement method {@link Lock#unlock}.
         *
         * @param arg the release argument.  This value is conveyed to
         *        {@link #tryRelease} but is otherwise uninterpreted and
         *        can represent anything you like.
         * @return the value returned from {@link #tryRelease}
         */
        public final boolean release(int arg) {
            // 尝试去释放
            if (tryRelease(arg)) {
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }
    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;
            }
    // 这段代码和上面的一致，只是为了顺序性，又拷下来看下

    public final boolean release(int arg) {
            // 尝试去释放，如果是完全释放，返回的就是true，否则是false
            if (tryRelease(arg)) {
                Node h = head;
                // 这里判断头结点是否为空以及waitStatus的状态，前面说了head节点其实是
                // 在第二个线程进来的时候初始化的，如果是空的话说明没后续节点，并且waitStatus
                // 也表示了后续的等待状态
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }

    /**
         * Wakes up node's successor, if one exists.
         *
         * @param node the 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);
        }

Related Posts

- AQS篇二 之 Condition 浅析笔记
- AbstractQueuedSynchronizer
- Leetcode 160 相交链表(intersection-of-two-linked-lists) 题解分析
- Leetcode 121 买卖股票的最佳时机(Best Time to Buy and Sell Stock) 题解分析
- Leetcode 2 Add Two Numbers 题解分析

请我喝杯咖啡

WeChat Pay

Alipay

- Post author: Nicksxs
- Post link: https://nicksxs.me/2021/02/14/AQS篇一/
- Copyright Notice: All articles in this blog are licensed under BY-NC-SA unless stating additionally.