JUC AQS源码分析(上) -- AQS原理分析

该系列博客主要分为上中下三篇分别介绍:AQS框架的原理、ReentrantLock上锁源码解析、ReentrantLock解锁源码解析

一、介绍

由于synchronize关键字在jdk1.6之前为重量级锁效率很低,每次对线程进行操作都需要涉及操作系统层面,于是并发大佬 Doug Lea 设计了JUC包,将线程同步操作提升到jdk级别,最差也是jvm级别。

队列同步器 AbstractQueuedSynchronizer,是用来构建锁或者其他同步器组件的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作,Doug Lea 期望他能够成为实现大部分同步需求的基础。

二、原理

AQS核心思想:
如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

CLH(Craig,Landin,and Hagersten)队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的关联关系)。

AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node)来实现锁的分配。

private transient volatile Node head; //队首
private transient volatile Node tail;//尾
private volatile int state;//锁状态,加锁成功则为1,重入+1 解锁则为0

AQS队列示意图:
在这里插入图片描述
Node类设计:

static final class Node {
	volatile int waitStatus;
	volatile Node prev;
	volatile Node next;
	volatile Thread thread;
}

简单来说:AQS将请求共享资源的线程封装成队列的结点(Node),通过CAS、自旋以及LockSupport.park()的方式,维护state变量的状态,使并发达到同步的效果。

维护state变量的状态,AQS提供了如下3种方法:

protected final int getState() {
        return state;
}
protected final void setState(int newState) {
        state = newState;
}
// 通过CAS,原子性的修改同步状态
protected final boolean compareAndSetState(int expect, int update) {
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

三、AQS对资源共享的两种方式

  • Exclusive(独占):只有一个线程能执行,如ReentrantLock。又可分为公平锁和非公平锁。
    • 公平锁:按照线程在队列中的排队顺序,先到者先拿到锁
    • 非公平锁:当线程要获取锁时,无视队列顺序直接去抢锁,谁抢到就是谁的
  • Share(共享):多个线程可同时执行,如Semaphore、CountDownLatch、 CyclicBarrier、ReadWriteLock。

ReentrantReadWriteLock 可以看成是组合式,因为ReentrantReadWriteLock也就是读写锁允许多个线程同时对某一资源进行读,但又只允许一个线程进行写。

四、AQS采用模板方法模式

AQS设计底层基于模板方法模式:
使用者需要继承同步器并重写指定的方法,随后将同步器组合在自定义同步组件的实现中,并调用同步器提供的模板方法,而这些模板方法将会调用使用者从写的方法。

简单来说:
AQS框架提供了固定的代码逻辑模板方法,但其中的某些细节方法需要自己实现,而这些实现细节的方法,就是根据不同同步组件的功能来维护state的状态。

同步器可重写的方法:

方法名称 描述
protected boolean tryAcquire(int arg) 独占式获取同步状态,实现该方法需要查询当前状态并判断同步状态是否符合预期,然后在进行CAS设置同步状态
protected boolean tryRelease(int arg) 独占式释放同步状态,等待获取同步状态的线程将有机会获取获取同步状态
protected int tryAcquireShared(int arg) 共享式获取同步状态,返回大于等于0的值,表示获取成功,反之,获取失败
protected boolean tryReleaseShared(int arg) 共享式释放同步状态
protected boolean isHeldExclusively() 当前同步器是否在独占模式下被线程占用,一般该方法表示是否被当前线程所独占

AQS提供的模板方法较多,下面就简单列举几个常用的方法,想要自定义同步组件,就需要调用AQS提供的模板方法来实现。

方法名称 描述
public final void acquire(int arg) 独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,将会进入同步队列等待,该方法将会调用重写的tryAcquire(int arg)方法
public final boolean release(int arg) 独占式的释放同步状态,该方法会在释放同步状态之后,将同步队列中第一个排队结点包含的线程唤醒
发布了120 篇原创文章 · 获赞 16 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_43327091/article/details/104083581