美文网首页
ReentrantLock与AQS

ReentrantLock与AQS

作者: 孤独的死锁 | 来源:发表于2020-11-13 19:28 被阅读0次

    AQS是J.U.C的基础,J.U.C中的很多并发工具,例如ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore等都是基于AQS来实现。

    AQS中维护了一个volatile的整型变量state,和一个FIFO的同步队列(使用双向链表实现)。线程通过对state进行CAS操作来获取锁,获取锁失败后会被加入到同步队列中,调用park进行挂起,等待锁被释放后被其他线程唤醒。

    AQS

    双向队列中的元素为Node,Node的结构定义如下:

    static final class Node {

    static final int CANCELLED =1; //表示线程获取锁的请求已经取消了

    static final int SIGNAL = -1; //表示线程已经准备好了,就等资源释放了

    static final int CONDITION = -2;//表示节点在等待队列中,节点线程等待唤醒

    static final int PROPAGATE = -3; //当前线程处在SHARED情况下,该字段才会使用

    volatile int waitStatus;  //当前节点在队列中的状态,上面的几个常量int值为waitStatus的几个状态

    volatile Node prev; //前驱指针

    volatile Node next;//后继指针

    volatile Thread thread; //表示处于该节点的线程

    Node nextWaiter;//指向下一个处于CONDITION状态的节点

    }

    非公平锁加锁过程:

    ReentrantLock.lock()  ->  NonfairSync.lock() :

    1、 对state变量进行cas操作争抢锁,cas成功,setExclusiveOwnerThread为当前线程,取锁成功

    2、cas失败,执行AQS的acquire方法

    ① nonfairSync.tryAcquire():如果state等于0,再次进行cas操作,成功则获取锁,返回true;如果state不等于0,检查当前线程是否为线程,如果为本线程,state自增,返回true;否则返回false; 

    ② 若①返回false,则调用addWaiter方法将本线程封装为Node节点进行入队操作:若队列没有元素,则新建一个虚节点作为Head节点,使用CAS将当前线程加入到队列的队尾。调用acquireQueue判断当前传入的Node对应的前置节点是否为head,如果是则尝试加锁。加锁成功过则将当前节点设置为head节点,然后空置之前的head节点,方便后续被垃圾回收掉。如果加锁失败或者Node的前置节点不是head节点,就会通过shouldParkAfterFailedAcquire方法将head节点的waitStatus变为了SIGNAL=-1,最后执行parkAndChecknIterrupt方法,调用LockSupport.park()挂起当前线程

    非公平锁解锁过程:

    ReentrantLock.unLock()  ->  AQS.release() :

    1、调用nonfairSync的tryRelease()方法,将state进行自减,如果state==0,则setExclusiveOwnerThread为null,释放锁,return true;若state不为0,则return false;

    2、若tryRelease返回true,则继续判断head节点的waitStatus是否为0,前面我们已经看到过,head的waitStatue为SIGNAL(-1),这里就会执行unparkSuccessor():将head节点的waitStatus设置为0,重新将head指针指向next节点,且使用LockSupport.unpark next节点。被唤醒的线程会接着尝试获取锁,用CAS指令修改state数据,接着之前被park的地方继续执行,继续执行acquireQueued()方法。

    相关文章

      网友评论

          本文标题:ReentrantLock与AQS

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