源码分析:同步基础框架——AbstractQueuedSynchronizer(AQS)

AQS 全称是 AbstractQueuedSynchronizer,位于java.util.concurrent.locks 包下面,AQS 提供了一个基于FIFO的队列和维护了一个状态state变量赖表示状态,可以作为构建或者其他相关同步装置的基础框架。AQS 支持两种模式:共享模式 和 排他模式,当它被定义为一个排他模式时,其他线程对其的获取就被阻止,而共享模式对于多个线程获取都可以成功。之所以说它是一个同步基础框架是因为很多同步类里面都用到了AQS,比如 ReentrantLock 中的内部类同步器Sync继承至AQS,ReentrantReadWriteLock中的同步器也是继承至AQS,还有 Semaphore 、CountDownLatch等都是基于AQS来实现的。

核心源码 类结构

AQS 继承了 AbstractOwnableSynchronizer, AbstractOwnableSynchronizer 这个类比较简单,就一个属性 private transient Thread exclusiveOwnerThread ,用来标识当前独占的持有者线程,通俗的说就是哪个线程拿到了独占锁,就调用AbstractOwnableSynchronizer 的方法把这个线程保存起来。源码如下:

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable { ... } public abstract class AbstractOwnableSynchronizer implements java.io.Serializable { private transient Thread exclusiveOwnerThread; // 构造方法,get set 方法省略。。。 }

后面的分析中,会有大量的同步器在获得锁之后会调用setExclusiveOwnerThread(Thread) 方法来保存锁的持有者线程;

重要内部类Node static final class Node { volatile int waitStatus; volatile Node prev; volatile Node next; volatile Thread thread; Node nextWaiter; }

以上五个成员变量主要负责保存该节点的线程引用,同步队列的前驱和后继节点,同时也包括了同步状态。

属性解释:

waitStatus:表示节点的状态。其中包含的状态有:

CANCELLED,值为1,表示当前的线程被取消;

SIGNAL,值为-1,表示当前节点的后继节点包含的线程需要运行,也就是unpark;

CONDITION,值为-2,表示当前节点在等待condition,也就是在condition队列中;

PROPAGATE,值为-3,表示当前场景下后续的acquireShared能够得以执行;

值为0,表示当前节点在sync队列中,等待着获取锁。

prev:前驱节点,比如当前节点被取消,那就需要前驱节点和后继节点来完成连接

next:后继节点

thread:入队列时的当前线程

nextWaiter:存储condition队列中的后继节点

重要属性:同步队列和同步状态

节点成为同步队列和 condition 条件队列构建的基础,同步器拥有三个成员变量:头结点head、尾节点tail和同步状态state。

private transient volatile Node head; private transient volatile Node tail; private volatile int state;

对于新的获取锁请求,形成Node节点,挂载到队列的尾部;对于锁资源的释放都是从队列的头部进行操作的。

+------+ prev +-----+ +-----+ head | | <---- | | <---- | | tail +------+ +-----+ +-----+ 可以重写的API

实现自定义同步器时,需要使用同步器提供的getState()、setState()和compareAndSetState()方法来控制同步状态。

方法1:protected boolean tryAcquire(int arg)

描述:已排它模式获取同步状态。这个方法的实现需要查询前状态是否允许获取,然后再进compareAndSetState()修改状态,修改成功代表成功获得锁。

方法2:protected boolean tryRelease(int arg)

描述:释放锁,也就是释放同步状态state的值到初始状态,一般是0。

方法3:protected int tryAcquireShared(int arg)

描述:共享模式下获取同步状态,一般可以用来做共享锁,或者用作限制资源最多同时被访问多少次。

方法4:protected boolean tryReleaseShared(int arg)

描述:共享模式下释放同步状态。

方法5:protected boolean isHeldExclusively()

描述:在排它模式下,返回同步状态是否被占用,比如我们可以实现返回逻辑为 getState() == 1,为true的话说明资源已经被占用了。

其他代码我们通过自己实现简单的排他锁案例来进行具体的详细分析

基于AQS实现的排他锁

一、定义一个MyAQSLock类

public class MyAQSLock{ }

二、定义一个内部类Sync做为同步器,继承自AbstractQueuedSynchronizer

public class MyAQSLock{ class Sync extends AbstractQueuedSynchronizer{ } }

三、重写同步器部分API

因为我们要实现的是排它锁的功能,意思就是同一时刻只能有一个线程获得锁,所以只需要重写tryAcquire、tryReleaseisHeldExclusively方法即可。

class Sync extends AbstractQueuedSynchronizer{ @Override protected boolean **tryAcquire**(int acquires){ // 入参只能为1 **assert acquires == 1; // 使用CAS的方式修改state值,修改成功代表成功获得锁 if(compareAndSetState(0,1)){ // 修改锁的持有者为当前线程 setExclusiveOwnerThread(Thread.currentThread()); // 返回true,表示成功获得锁 return true; } // 返回false,没有获得锁 return false; } @Override protected boolean **tryRelease**(int releases){ assert releases == 1; if (getState() == 0){ // 已经被释放了 throw new IllegalMonitorStateException(); } // lock() 和 unlock() 一般都是成对出现的,所以这里不需要同步语句,可以直接修改state值为0 setState(0); return true; } @Override protected boolean **isHeldExclusively**() { // 返回true,说明已经有其他线程获得锁 return getState() == 1; } }

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wssyjz.html