AQS入门

本文详细介绍AQS相关的知识

概述

  AQS全称:Abstract Quened Synchronizer

思路

  1.学习AQS的主要目的是了解其原理

  2.提供自我技术水平

  3.应对面试

  4.先了解其场景,再学习如何使用,再掌握其原理

为什么要学习AQS

  我们发现ReentrantLock和Semaphore有个共同的特点,即为闸门,都有协作,提取其工具类变成了AQS

同步类和AQS的关系

  Semaphore与AQS关系如下图所示:

  

 

 

  CountDownLatch为AQS关系如下图所示:

  

 

 

  ReentrantLock与AQS关系如下图:

  

 重要性

  我们查看下那些类都使用了AQS,见下截图。

  

 AQS核心部分

  1.state:实现类不同则含义不同,在Semaphore中代表剩余的许可证的数量;在CountDownLatch代表还需要倒数的数量

    相关的方法如下:

    

 

 

 

  2.控制线程配合和抢锁的FIFO队列

    此队列用来存放等待线程的队列,双向的链表

  3.获取或者释放的重要方法

    不同的实现类对应的获取和实现方法都不相同。

  AQS用法

    我们从CountDownLath、Semaphore、ReentrantLock中分析AQS的用法

    CountDownLath中AQS用法

      我们从构造、计数、countDown和await进行分析,首先我们看下构造:构造时,我们需要复制sycn,我们将count设置成了state

      

      

 

 

 

 

 

          我们在分析类的继承关系:

          

 

 

        接下来我们查看下await方法,如下所示:

        

 

        

 

 

 

          

 

 

                           

 private boolean doAcquireSharedNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (nanosTimeout <= 0L)
            return false;
        final long deadline = System.nanoTime() + nanosTimeout;
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head) {
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return true;
                    }
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

  

 

    Semaphore中AQS用法

      我们重点分析下信号量中是如何获取的,即require方法的实现。

      

 

 

final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

      上述代码是非公平时,获取信号量的方法,从代码中我可以看到使用了CAS的自旋操作,首先我们获取目前系统中剩余的信号量即为availabe;减去需要申请的信号量,

      remaining代表剩余的信号量,当剩余信号量为负数时,说明此时申请失败,继续下一次的循环,当剩余信号量remaining大于等于0时,说明可以申请了,

                     继续执行CAS

      操作修改state值,若修改成功,则获取成功;若修改失败,则代表被其他线程抢走了,继续执行循环直到申请到为止。

    ReentrantLock中AQS用法

      我们熟知ReenTrantLock是一把可重入锁,本实例我们从非公平的情况下进行分析,首先我看ReenTrantLock内部类的结构或者相关的方法。如下图所示:

      

 

         接下来我们分析非公平的加锁的实现。

        

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

        从代码结构中可以看到:首先我们执行CAS的加锁操作,将state=0 修改成state=1;

               若修改成功,则标记当前线程为锁的持有者;

               若修改失败,则我们基于执行acquire方法,

    接下来我们分析acquire方法的实现:

    

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

      下面代码为tryAcquire的实现。

      我们可以看到:首先我获取state的值,如果state=0;说明锁没有被占用,然后CAS操作,设置当前线程持有锁;

                       如果state不等于0,判断是不是持有者,如果是,则进行重入操作,state+1;

       

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

          下面代码为释放锁的代码实现,如下图:

    

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

      我们发现释放锁的时候,需要判断当前线程是不是持有锁,否则会抛出异常;若c=0,则直接释放;否则重入次数减去1

              

  

posted @ 2020-04-26 08:16  cnxieyang  阅读(267)  评论(0编辑  收藏  举报
联系邮箱:cnxieyang@163.com