美文网首页
AQS同步状态的获取和释放

AQS同步状态的获取和释放

作者: T_log | 来源:发表于2018-07-03 17:53 被阅读22次

AQS同步状态的获取和释放

个人感觉

其实了解AQS应该从同步状态的获取和释放开始,但是看了源码后,绝对会有一种不知道从哪里开始的感觉,后来还是觉得从addWaiter将节点加入CLH队列开始,一点一点的去了解

  1. 独占式和共享式获取同步状态

独占式

所谓独占式同步状态的获取和释放,属于我们平常所说的悲观锁
这样的话,独占式获取同步状态,每次只有一个线程获取同步状态
由于悲观锁的加锁策略,如果有两个读线程同时获取共享状态的话,只会有一次线程获取同步状态,另外一个必须阻塞等待,势必会对性能造成影响

共享式

相对于独占式,允许多个读线程同时获取锁,并发的访问共享资源,但是不允许在写线程进行时,有读线程获取锁
属于乐观锁,相比较悲观锁的严格的加锁策略,乐观锁的加锁策略相对宽松。

  1. 获取同步状态
    AQS的入口肯定是acquire获取同步状态
 /**
    * 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}.
    *
/
public final void acquire(int arg) {
        //1、tryAcquire 尝试获取同步状态,成功则返回,在获取同步状态时,需保证线程安全的获取
        if (!tryAcquire(arg) &&
            //2、如果获取失败,则调用addWaiter方法将线程加入CLH队列(其实是包含当前线程的节点)
            //3、调用acquireQueued将包含当前线程的节点,自旋的方式去获取同步状态
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //4、Thread.currentThread().interrupt();中断当前线程
            selfInterrupt();
    }

解释方法的注释开始吧(纯个人)

在独占模式下获取同步状态,不响应中断(即:在获取同步状态的时候,如果线程被中断或者取消,线程并没有从CLH队列中移除)
该方法最少会执行一次tryAcquire方法,成功则返回
tryAcquire失败,包含该线程,即status会被构造成一个新的节点,加入到CLH队列中
CLH队列中的每个节点会自旋(死循环),获取同步状态

  1. acquireQueued
/**
     * 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) {
        //操作标识,会再finally中使用到
        boolean failed = true;
        try {
            //中断标识
            boolean interrupted = false;
            for (;;) {
                //当前节点的前驱节点
                final Node p = node.predecessor();
                //如果当前节点的前驱节点是头结点,则tryAcquire尝试获取同步状态
                if (p == head && tryAcquire(arg)) {
                    //设置当前节点为头结点
                    setHead(node);
                    //取消前驱节点对当前节点next的引用,有利于GC回收头结点
                    p.next = null; // help GC
                    //标识为False,在finally的时候不会取消同步状态的获取
                    failed = false;
                    //返回false,即中断状态
                    return interrupted;
                }
                //同步状态获取失败的话,则线程等待,这个方法应该是重点
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

解释

已经在CLH队列中的、在独占模式下、非中断的线程,获取同步状态,只有该节点的前驱节点是头结点时,
就CLH队列的FIFO的特性,不难理解为什么前驱节点是头结点时,才开始尝试获取同步状态

  1. 响应中断式获取同步状态
  • acquire采用了独占式获取同步状态,不响应中断,在获取同步状态时,如果线程被中断或者被取消,节点仍然在CLH队列中,依然会尝试获取同步状态
/**
* 1、 独占模式下获取同步状态
* 2、 响应中断,如果被中断,则中止线程
* 3、 最少会执行一次tryAcquire,成功则返回
*
*
public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        //如果线程被中断,则抛出InterruptedException异常
        if (Thread.interrupted())
            throw new InterruptedException();
        //调用tryAcquire尝试获取同步状态
        if (!tryAcquire(arg))
            //获取失败,则调用doAcquireInterruptibly
            doAcquireInterruptibly(arg);
    }

和acquire不同的是,
1、在获取同步状态时,会判断当前线程是否被中断,如果被中断,则配出InterruptedException,
2、tryAcquire失败后,直接调用doAcquireInterruptibly加入CLH队列并自旋获取同步状态,取消了中断标识,改成了直接抛出异常

 /**
  * Acquires in exclusive interruptible mode.
  * @param arg the acquire argument
  */
 private void doAcquireInterruptibly(int arg)
     throws InterruptedException {
     //将线程加入CLH队列中(独占模式)
     final Node node = addWaiter(Node.EXCLUSIVE);
     //操作标识
     boolean failed = true;
     try {
         //自旋,死循环
         for (;;) {
             //当前节点的前驱节点p
             final Node p = node.predecessor();
             //如果当前节点的前驱节点是头结点,则tryAcquire尝试获取同步状态
             if (p == head && tryAcquire(arg)) {
                 //设置当前节点为头结点
                 setHead(node);
                 //前驱节点释放对当前节点的引用,有利于GC 进行回收
                 p.next = null; // help GC
                 //操作标识为false
                 failed = false;
                 return;
             }
             //如果获取失败,则shouldParkAfterFailedAcquire,还是细说吧
             if (shouldParkAfterFailedAcquire(p, node) &&
                 parkAndCheckInterrupt())
                 //这里其实就是和acquireQueued中的区别所在,acquireQueued采用了中断标识interrupted,而相应中断直接抛出异常
                 throw new InterruptedException();
         }
     } finally {
         if (failed)
             cancelAcquire(node);
     }
 }
  1. 独占式超时获取
/**
     * Attempts to acquire in exclusive mode, aborting if interrupted,
     * and failing if the given timeout elapses.  Implemented by first
     * checking interrupt status, then invoking at least once {@link
     * #tryAcquire}, returning on success.  Otherwise, the thread is
     * queued, possibly repeatedly blocking and unblocking, invoking
     * {@link #tryAcquire} until success or the thread is interrupted
     * or the timeout elapses.  This method can be used to implement
     * method {@link Lock#tryLock(long, TimeUnit)}.
     * 1、独占模式下获同步状态
     * 2、如果被中断,则线程中止
     * 3、线程首先判断是否被中断
     * 4、至少会调用一次tryAcquire尝试获取同步状态
     * 5、相比较acquireInterruptibly,新增了对时间的控制
     */
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }
/**
     * Acquires in exclusive timed mode.
     *
     * @param arg the acquire argument
     * @param nanosTimeout max wait time
     * @return {@code true} if acquired
     */
    private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        //超时控制
        if (nanosTimeout <= 0L)
            return false;
        //唤醒时间
        final long deadline = System.nanoTime() + nanosTimeout;
        //将当前线程构建成新的节点,加入到CLH队列中
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                //当前节点的前驱节点
                final Node p = node.predecessor();
                //如果当前节点的前驱节点是头结点,则尝试获取同步状态
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return true;
                }
                //剩余时间
                nanosTimeout = deadline - System.nanoTime();
                //已经超时,返回false
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)//如果剩余时间大于快速自旋剩余时间,则休眠,如果小于快速自旋剩余时间则直接进行自旋
                    LockSupport.parkNanos(this, nanosTimeout);
                //线程如果被中断,则抛出InterruputException异常
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
  1. 独占式同步状态释放
public final boolean release(int arg) {
        //尝试释放同步状态
        if (tryRelease(arg)) {
            //释放成功,调用unparkSuccessor对后继节点进行唤醒动作
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
  1. 共享式获取同步状态
/**
     * Acquires in shared mode, ignoring interrupts.  Implemented by
     * first invoking at least once {@link #tryAcquireShared},
     * returning on success.  Otherwise the thread is queued, possibly
     * repeatedly blocking and unblocking, invoking {@link
     * #tryAcquireShared} until success.
     * 1、共享模式下获取同步状态,不响应中断
     * 2、最少会调用一次tryAcquireShared,成功则返回
     * 3、否则线程入队,执行tryAcquireShared直到成功为止
     */
    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }
/**
     * Acquires in shared uninterruptible mode.
     * @param arg the acquire argument
     */
    private void doAcquireShared(int arg) {
        //加入到CLH队列中
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            //中断标识
            boolean interrupted = false;
            for (;;) {
                //当前节点的前驱节点
                final Node p = node.predecessor();
                //前驱节点如果是头结点
                if (p == head) {
                    //尝试获取同步状态
                    int r = tryAcquireShared(arg);
                    //如果r>0,则说明获取成功
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
  1. 写在后面
  • 胆小善良的我还总想干点大事
  • 不要卑微了自己

相关文章

  • AQS同步状态的获取和释放

    AQS同步状态的获取和释放 个人感觉其实了解AQS应该从同步状态的获取和释放开始,但是看了源码后,绝对会有一种不知...

  • AbstractQueuedSynchronizer 队列同步器

    AQS 共享式同步状态获取和释放 上一篇文章中主要分析了 AQS 的独占模式对同步状态的获取和释放过程,本文主要分...

  • AQS之独占式同步状态的获取和释放

    上一篇文章LZ分析了AQS中的同步队列,这一章LZ将分析AQS中独占式获取同步状态和释放。AQS提供提供的独占式获...

  • 理解ReentrantLock的公平锁和非公平锁

    学习AQS的时候,了解到AQS依赖于内部的FIFO同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器...

  • AQS之共享式同步状态的获取和释放

    前面LZ介绍了独占式获取同步状态和释放,这一章LZ将介绍共享式同步状态的获取和释放。相比于独占式同一时刻只能有一个...

  • AQS

    AQS官方解读 AQS 只是一个框架,具体资源的获取/释放方式交由自定义同步器去实现,AQS 这里只定义了一个接口...

  • AQS之阻塞和唤醒线程

    在前面的文章中介绍了独占式同步状态的获取和释放以及共享式同步状态的获取和释放,在前面的文章中并没有介绍线程的阻塞和...

  • Java多线程(9)

    Java多线程(9) AQS(2) 锁的占有与释放 对于AQS来说,线程同步的关键是对状态值state进行操作,根...

  • AQS

    AQS是阻塞式锁和相关同步器框架。 特点: 使用state来表示资源状态,子类维护状态,通过状态控制是否获取到锁g...

  • AQS的节点(Node)实现原理

    在AQS中有一个静态内部类Node,node包含了获取同步状态失败的线程引用、等待状态、前驱节点和后继节点,节点的...

网友评论

      本文标题:AQS同步状态的获取和释放

      本文链接:https://www.haomeiwen.com/subject/rsxzyftx.html