AQS原理与源码

Posted 好好学习312

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AQS原理与源码相关的知识,希望对你有一定的参考价值。

一、细说AQS

      在深入分析AQS之前,我想先从AQS的功能上说明下AQS,站在使用者的角度,AQS的功能可以分为两类:独占锁和共享锁。它的所有子类中,要么实现并使用了它独占锁的API,要么使用了共享锁的API,而不会同时使用两套API,即便是它最有名的子类ReentrantReadWriteLock,也是通过两个内部类:读锁和写锁,分别实现的两套API来实现的,到目前为止,我们只需要明白AQS在功能上有独占锁和共享锁两种功能即可。

二、ReentrantLock的调用过程

     经过观察ReentrantLock把所有Lock接口的操作都委派到一个Sync类上,该类继承了AbstractQueuedSynchronizer:

[java]   view plain  copy
  1. abstract static class Sync extends AbstractQueuedSynchronizer  

Sync又有两个子类:

[java]   view plain  copy
  1. /** 
  2.      * Sync object for non-fair locks 
  3.      */  
  4.     static final class NonfairSync extends Sync  

[java]   view plain  copy
  1. /** 
  2.     * Sync object for fair locks 
  3.     */  
  4.    static final class FairSync extends Sync  
显然是为了支持公平锁和非公平锁而定义,默认情况下为非公平锁。
先理一下Reentrant.lock()方法的调用过程(默认非公平锁):


AbstractQueuedSynchronizer中抽象了绝大多数Lock的功能,而只把tryAcquire方法延迟到子类中去实现。tryAcquire方法的语义在于用具体子类判断请求线程是否可以获得锁,无论成功与否AbstractQueuedSynchronizer都将处理后面的流程。

三、获取锁的过程

      简单说来,AbstractQueuedSynchronizer会把所有的请求线程构成一个CLH队列,当一个线程执行完毕(lock.unlock())时会激活自己的后继节点,但正在执行的线程并不在队列中,而那些等待执行的线程全部处于阻塞状态。


四、非公平锁的加锁流程

(1). 首先我们分析非公平锁的的请求过程。我们假设在这个时候,还没有任务线程获取锁,这个时候,第一个线程过来了(我们使用的是非公平锁),那么第一个线程thread1会去获取锁,这时它会调用下面的方法,通过CAS的操作,将当前AQS的state由0变成1,证明当前thread1已经获取到锁,并且将AQS的exclusiveOwnerThread设置成thread1,证明当前持有锁的线程是thread1。:

[java]   view plain  copy
  1. /** 
  2.  * Performs lock.  Try immediate barge, backing up to normal 
  3.  * acquire on failure. 
  4.  */  
  5. final void lock()   
  6.     // 如果锁没有被任何线程锁定且加锁成功则设定当前线程为锁的拥有者  
  7.     if (compareAndSetState(01))  
  8.         setExclusiveOwnerThread(Thread.currentThread());  
  9.     else  
  10.         acquire(1);  
  11.   

(2). 此时来了第二个线程thread2,并且我们假设thread1还没有释放锁,因为我们使用的是非公平锁,那么thread2首先会进行抢占式的去获取锁,调用NonFairSync.lock方法获取锁。NonFairSync.lock方法的第一个分支是通过CAS操作获取锁,很明显,这一步肯定会失败,因为此时thread1还没有释放锁。那么thread2将会走NonFairSync.lock方法的第二个分支,进行acquire(1)操作。acquire(1)其实是AQS的方法,acquire(1)方法内部首先调用tryAcquire方法,ReentrantLock.NonFairLock重写了tryAcquire方法,并且ReentrantLock.NonFairLock的tryAcquire方法又调用了ReentrantLock.Sync的nonfairTryAcquire方法,nonfairTryAcquire方法如下:

[java]   view plain  copy
  1. /** 
  2.         * Performs non-fair tryLock.  tryAcquire is 
  3.         * implemented in subclasses, but both need nonfair 
  4.         * try for trylock method. 
  5.         */  
  6.        final boolean nonfairTryAcquire(int acquires)   
  7.            final Thread current = Thread.currentThread();  
  8.            int c = getState();  
  9.            if (c == 0)   
  10.                if (compareAndSetState(0, acquires))   
  11.                    setExclusiveOwnerThread(current);  
  12.                    return true;  
  13.                  
  14.              
  15.            else if (current == getExclusiveOwnerThread())   
  16.                int nextc = c + acquires;  
  17.                if (nextc < 0// overflow  
  18.                    throw new Error("Maximum lock count exceeded");  
  19.                setState(nextc);  
  20.                return true;  
  21.              
  22.            return false;  
  23.          

这个方法的执行逻辑如下:

1. 获取当前将要去获取锁的线程,在此时的情况下,也就是我们的thread2线程。

2. 获取当前AQS的state的值。如果此时state的值是0,那么我们就通过CAS操作获取锁,然后设置AQS的exclusiveOwnerThread为thread2。很明显,在当前的这个执行情况下,state的值是1不是0,因为我们的thread1还没有释放锁。

3. 如果当前将要去获取锁的线程等于此时AQS的exclusiveOwnerThread的线程,则此时将state的值加1,很明显这是重入锁的实现方式。在此时的运行状态下,将要去获取锁的线程不是thread1,也就是说这一步不成立。

4. 以上操作都不成立的话,我们直接返回false。

既然返回了false,那么之后就会调用addWaiter方法,这个方法负责把当前无法获取锁的线程包装为一个Node添加到队尾。通过下面的代码片段我们就知道调用逻辑:

[java]   view plain  copy
  1. public final void acquire(int arg)   
  2.         if (!tryAcquire(arg) &&  
  3.             acquireQueued(addWaiter(Node.EXCLUSIVE), arg))  
  4.             selfInterrupt();  
  5.       

我们进入到addWaiter方法内部去看:

[java]   view plain  copy
  1. private Node addWaiter(Node mode)   
  2.         Node node = new Node(Thread.currentThread(), mode);  
  3.         // Try the fast path of enq; backup to full enq on failure  
  4.         Node pred = tail;  
  5.         ifAQS 原理解析以及源码分析

    源码|并发一枝花之ReentrantLock与AQS:lockunlock

    源码|并发一枝花之ReentrantLock与AQS:lockunlock

    AQS(AbstractQueuedSynchronizer)源码深度解析—共享式获取锁释放锁的原理一万字

    AQS(AbstractQueuedSynchronizer)源码深度解析—同步队列以及独占式获取锁释放锁的原理一万字

    Java并发之AQS源码分析