> 文档中心 > 并发编程核心底层AQS你知道多少

并发编程核心底层AQS你知道多少

一、你知道AQS吗?核心思想是什么?干嘛用的?

二、源码看下重要的几个方法以及流程步骤

三、​你知道的AQS有几种同步方式,实现同步器一般要覆盖哪些方法


面试官:AQS你知道的吧,简单介绍一下

面试的时候被面试官这么猛的一问,支支吾吾半天没有很清晰的思路来做一个很好的回答,本博文重点针对AQS是什么,干什么用的,核心思想是什么,加锁/解锁的步骤是哪些、实现同步器的话哪些核心方法是必不可少的进行了描述,要耐心看完呀~

一、你知道AQS吗?核心思想是什么?干嘛用的?

          AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。它是一个Java提高的底层同步工具类,虽然我们不会直接使用这个类,但是这个类是Java很多并发工具的底层实现,比如CountDownLatch、ReentrantLock,Semaphore,ReentrantReadWriteLock,SynchronousQueue,FutureTask等等皆是基于AQS的

就好比我们写代码,有很多框架,我们会对框架进行二次开发实现,就能写出很多的网站啊系统之类的出来,AQS就好比这种框架,不用考虑那么多底层的东西,直接用它提供的接口就行了,按照它的规范去重写对应的方法。
 

简单来说:是用一个int类型的变量表示同步状态,并提供了一系列的CAS操作来管理这个同步状态对象
一个是 state(用于计数器,类似gc的回收计数器)
一个是线程标记(当前线程是谁加锁的),
一个是阻塞队列(用于存放其他未拿到锁的线程)

举例;线程A调用了lock()方法,通过CAS将state赋值为1,然后将该锁标记为线程A加锁。如果线程A还未释放锁时,线程B来请求,会查询锁标记的状态,因为当前的锁标记为 线程A,线程B未能匹配上,所以线程B会加入阻塞队列,直到线程A触发了 unlock() 方法,这时线程B才有机会去拿到锁,但是不一定肯定拿到

二、源码看下重要的几个方法以及流程步骤

先看下这张图

首先从类的定义开始逐步的深入了解:

public abstract class AbstractQueuedSynchronizer    extends AbstractOwnableSynchronizer    implements java.io.Serializable {    //state变量表示锁的状态,0表示未锁定 大于0表示已锁定,这个值可以用来实现锁的【可重入性】     //同时这个变量还是用volatile关键字修饰的,保证可见性    private volatile int state; //等待队列的头节点,只能通过setHead方法修改,如果head存在,能保证waitStatus状态不为CANCELLED    private transient volatile Node head;    // 等待队列的尾结点,只能通过enq方法来添加新的等待节点    private transient volatile Node tail;}

 acquire(int arg) 源码分析,好比加锁lock操作。成功后就直接返回,失败后就通过addWaiter方   法把当前线程封装成一个Node,加到队列的尾部,再通过acquireQueued方法尝试获取同步锁,   成功获取锁的线程的Node节点会被移出队列。

注意: 线程获取锁成功后 直接返回,不会进入等待队列里面,只有失败的时候才会

//该方法主要调用tryAcquire方法尝试获取锁,成功返回true,失败就将线程封装成Node对象,放入队列。public final void acquire(int arg) {    if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //如果以上条件都满足,会执行selfInterrupt方法中断当前线程。 selfInterrupt();}

 tryAcquire()尝试直接去获取资源,如果成功则直接返回,AQS里面未实现但没有定义成abstract,因为独占模式下只用实现tryAcquire-tryRelease,而共享模式下只用实现tryAcquireShared-tryReleaseShared,类似设计模式里面的适配器模式

//在此处是空实现,因为AQS可以构造很多种锁,比如独占锁,共享锁,这个tryAcquire 是基于独占锁。用了适配器模式protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException();    }

addWaiter() 根据不同模式将线程加入等待队列的尾部,有Node.EXCLUSIVE互斥模式、Node.SHARED共享模式;如果队列不为空,则以通过compareAndSetTail方法以CAS将当前线程节点加入到等待队列的末尾。否则通过enq(node)方法初始化一个等待队列

   // 1、Node.EXCLUSIVE:独占模式   // 2、Node.SHARED:共享模式   private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode);  // 尝试快速添加尾结点,失败就执行enq方法 Node pred = tail; if (pred != null) {     node.prev = pred; // CAS的方式设置尾结点     if (compareAndSetTail(pred, node)) {  pred.next = node;  return node;     } }    //通过enq(node)方法初始化一个等待队列 enq(node); return node;    }

  acquireQueued()使线程在等待队列中获取资源,一直获取到资源后才返回,如果在等待过程中被中断,则返回true,否则返回false

//获取失败则将当前线程封装为Node.EXCLUSIVE的Node节点插入AQS阻塞队列的尾部

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

加锁操作看完后,我们再来看下解锁的操作

release(int arg) 好比解锁unlock
    独占模式下线程释放指定量的资源,里面是根据tryRelease()的返回值来判断该线程是否已经完成释放掉资源了;在自义定同步器在实现时,如果已经彻底释放资源(state=0),要返回true,否则返回false  

    public final boolean release(int arg) { // 尝试释放锁 if (tryRelease(arg)) {     Node h = head;     if (h != null && h.waitStatus != 0)  // unparkSuccessor方法用于唤醒等待队列中下一个线程  unparkSuccessor(h);     return true; } return false;    }

三、​你知道的AQS有几种同步方式,实现同步器一般要覆盖哪些方法

独占式: 比如ReentrantLock​共享式:比如Semaphore​存在组合:组合式的如ReentrantReadWriteLock,AQS为使用提供了底层支撑,使用者可以自由组装实现​​1. boolean tryAcquire(int arg) //独占模式2. boolean tryRelease(int arg)  //独占模式3. int tryAcquireShared(int arg)  //共享模式4. boolean tryReleaseShared(int arg)  //共享模式5. boolean isHeldExclusively() //判断是哪种模式​不需要全部实现,根据获取的锁的种类可以选择实现不同的方法,比如实现支持独占锁的同步器应该实现tryAcquire、 tryRelease、isHeldExclusively实现支持共享获取的同步器应该实现tryAcquireShared、tryReleaseShared、isHeldExclusively