并发学习笔记——AQS 01

0 概述

详解文章

https://blog.csdn.net/TZ845195485/article/details/118517936
https://zhuanlan.zhihu.com/p/86072774
https://www.cnblogs.com/fsmly/p/11274572.html#_labelTop
https://blog.csdn.net/mulinsen77/article/details/84583716

AbstractQueueSynchronizer

  • AQS是一个工具,juc的很多锁都是基于AQS实现的,如ReentrantLock,CountDownLatch,Semaphore;
  • 核心变量 state,代表加锁的状态;不同的锁实现有不同的值;
  • tryAcquire 加锁/获取资源 具体需要子类实现
    • 加锁就是把Node节点里的thread设置为加锁线程,
    • state = state+arg
    • 加锁失败的线程会构建一个节点,然后将节点加入到等待队列里(每次都挂在队列尾部)
  • tryRelease 解锁/释放资源 具体需要子类实现
    • state = state-arg
    • 等待队列的结点一直在自旋(for (; ; ; ))
    • 等待占用资源的节点释放资源(队列头节点),不断尝试tryAcquire

英文

1 Node 节点的数据结构

volatile int waitStatus;
  • static final int CANCELLED = 1;
  • static final int SIGNAL = -1; 节点处于此状态意味着后继节点将被唤醒
  • static final int CONDITION = -2; 节点位于Condition队列
  • static final int PROPAGATE = -3;
volatile Thread thread;
volatile Node prev;
volatile Node next;
Node nextWaiter;

2 资源的获取与释放

同步队列与等待队列图解
https://blog.csdn.net/MakeContral/article/details/78135531


    /**
     * Head of the wait queue, lazily initialized.  Except for
     * initialization, it is modified only via method setHead.  Note:
     * If head exists, its waitStatus is guaranteed not to be
     * CANCELLED.
     */
    private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
     // 
    private volatile int state;

2.1 访问资源 acquire

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

2.1.1 tryAcquire(arg)

tryAcquire方法返回一个布尔值,true表示当前线程能够访问资源,false当前线程不能访问资源

2.1.2 addWaiter(Node mode)

为当前线程构造一个节点,然后将节点加入到 wait Queue 的末尾处

    /**
     * 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 = 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;
    }
enq(final Node node)
    /**
     * 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 (;;) {
            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;
                }
            }
        }
    }

2.1.3 acquireQueued(final Node node, int arg) 阻塞

节点被加到到等待队列末尾处后,调用此方法使其进入阻塞状态
https://www.jianshu.com/p/dcbcea767d69
下面这段话来自上面的博客
https://www.jianshu.com/p/dcbcea767d69

    /**
     * Acquires in exclusive uninterruptible mode for thread already in
     * queue. Used by condition wait methods as well as acquire.
     *
     * @param node the node
     * @param arg the acquire argument
     * @return {@code true} if interrupted while waiting
     */
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            // 自旋操作
            for (;;) {
                // 获取前驱节点
                final Node p = node.predecessor();
                // 如果前驱节点是头节点,并且获取锁成功,执行下面逻辑
                // 队列第一个节点是当前正在处理的,tryAcquire(arg)获取锁成功说明前一个已经处理完了,所以轮到当前节点
                if (p == head && tryAcquire(arg)) {
                   /* private void setHead(Node node) {
                    *  		head = node;
                    * 		node.thread = null;
                    *       node.prev = null;
                    * }
                    */
                    // 将head指针指向当前节点
                    setHead(node);
                    // 在sethead里 head已经不指向最初的头节点(也就是当前节点的前驱)
                    // 这里最初的头节点的后继指针也设置为null 那么这个节点彻底不被引用了
                    // 为了GC回收
                    p.next = null; // help GC
                    failed = false;
                    // 返回false表示不能被打断,意思是没有被挂起,也就是获得到了锁
                    return interrupted;
                }
                // 是否应该挂起(进入阻塞状态) 进入到这里说明获取锁失败 或者 p != head
                if (shouldParkAfterFailedAcquire(p, node) &&
                    // 检查是否被中断,如果是返回true
                    /**
                      private final boolean parkAndCheckInterrupt() {
                          LockSupport.park(this);
                          线程进入阻塞状态
                          return Thread.interrupted();
                      }
                    */
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

shouldParkAfterFailedAcquire

为了确保当前节点阻塞后,能被安全唤醒,将此节点挂在 状态为signal 节点后边

 /**
     * Checks and updates status for a node that failed to acquire.
     * 检查并且更新节点状态 如果获取锁失败
     * Returns true if thread should block. This is the main signal
     * 如果线程应该被阻塞返回true
     * control in all acquire loops.  Requires that pred == node.prev.
     *
     * @param pred node's predecessor holding status
     * @param node the node
     * @return {@code true} if thread should block
     */
    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.
             * 如果前驱节点的状态是 signal,可以安全的进行阻塞了(反正前驱节点会唤醒当前节点)
             * 
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             * 如果状态 > 0 ,前驱节点处于取消状态,跳过前驱节点
             * 直到找到一个状态不大于0的 
             * 将node挂到那个节点后边
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
           // 调用CAS 设置前驱结点的状态为signal
            /*
             * 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;
    }

2.2 释放资源release

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

unparkSuccessor

https://www.zhihu.com/question/50724462/answer/123776334


    /**
     * 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;
        // 如果状态码小于0 CAS设置状态码为0
        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;
        // 如果后继节点为null 或者状态码>0(取消状态)
        if (s == null || s.waitStatus > 0) {
        // 重置新建节点
            s = null;
            // 从尾节点向前扫描
            // 找到一个 状态码<=0的,并且不是当前节点 的 节点
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
            // 如果到了当前节点或者头节点 不会进入循环
            // t.prev != null 说明t不是head
        }
        // 唤醒这个节点
        if (s != null)
            LockSupport.unpark(s.thread);
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值