【JUC 并发编程】— AQS 概述与实例
作者:互联网
介绍
队列同步器 AbstractQueuedSynchronizer,简称为 AQS,是用来构建锁及其他同步组件(比如 ReentrantLock、CountDownLatch)的基础框架。它使用了一个 int 成员变量表示同步状态,通过内置的 FIFO 队列来完成获取资源线程的排队工作。AQS 的主要使用方式是继承,子类被推荐被定义为自定义同步组件的静态内部类,通过实现同步器的一些protected方法。 如果一个类想成为状态依赖的类,那么它必须拥有一些状态。AQS 负责管理同步器类的状态,可以通过 getState、setState 以及 compareAndSetState 等 protected 类型方法来进行操作。
如果某个同步器支持独占式操作,那么需要实现一些 protected 方法,包括 tryAcquire、tryRelease 和 isHeldExclusively 等;而对于支持共享获取的同步器,则应该实现 tryAcquireShared 和 tryReleaseShared 等方法。
AQS 是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。可以这样理解二者之间的关系:
琐是面向使用者的,它定义了使用者与锁交互的接口,隐藏了实现细节;同步器面向锁的,它简化了锁的实现方式,屏蔽了同步状态的管理、线程的排队、等待与唤醒等底层操作。锁和同步器很好的隔离了使用者和实现者所需关注的领域。
接口
同步器的设计设计是基于模板方式模式的,也就是说,使用者需要继承同步器并重写指定的方法。
同步状态相关方法
重写同步器指定的方法时,需要使用到同步器提供的一下三个方法来访问或修改同步状态:
/**
* 获取同步状态
* Returns the current value of synchronization state.
*
* 这个操作有 volatile 读的内存语义,也就是说总是从主内存获取,保证同步状态是最新的
* This operation has memory semantics of a <tt>volatile</tt> read.
* @return current state value
*/
protected final int getState() {
return state;
}
/**
* 设置同步状态
* Sets the value of synchronization state.
*
* 这个操作有 volatile 写的内存语义,也就是写完就同步到主内存
* This operation has memory semantics of a <tt>volatile</tt> write.
* @param newState the new state value
*/
protected final void setState(int newState) {
state = newState;
}
/**
* CAS 方式设置状态,有 volatile 的读写内存语义
* Atomically sets synchronization state to the given updated
* value if the current state value equals the expected value.
* This operation has memory semantics of a <tt>volatile</tt> read
* and write.
*/
protected final boolean compareAndSetState(int expect, int update) {
// See below for intrinsics setup to support this
return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
可重写方法
自定义同步组件内部静态类(推荐使用)需重写同步器 protected 方法如下:
方法名称 | 描述 |
---|---|
protected boolean tryAcquire(int arg) | 独占式获取同步状态,实现该方法需要判断当前的同步状态是否符合预期,然后再 CAS 设置同步状态 |
protected boolean tryRelease(int arg) | 独占式释放同步状态 |
protected int tryAcquireShared(int arg) | 共享式获取同步状态,返回 >=0 的值,表去获取成功,反之失败 |
protected int tryReleaseShared(int arg) | 共享式释放不同状态 |
protected boolean isHeldExclusively() | 当前同步器在独占式模式下是否被占用 |
模板方法
自定义同步组件时,将会调用同步器的模板方法,如下:
独占式
方法名称 | 描述 |
---|---|
void acquire(int arg) | 独占式获取同步状态,不响应中断 |
void acquireInterruptibly(int arg) | 与 acquire(int arg) 相同,但响应中断 |
boolean tryAcquireNanos(int arg, long nanosTimeout) | 在 acquireInterruptibly(int arg) 基础上增加超时限制 |
boolean release(int arg) | 独占式释放同步状态 |
共享式
方法名称 | 描述 |
---|---|
void acquireShared(int arg) | 共享式获取 |
void acquireSharedInterruptibly(int arg) | 与 acquireShared(int arg) 相同,响应中断 |
boolean tryAcquireSharedNanos(int arg, long nanosTimeout) | 在 acquireSharedInterruptibly(int arg) 基础上增加超时限制 |
boolean releaseShared(int arg) | 共享式释放 |
等待队列信息
方法名称 | 描述 |
---|---|
Collection |
获取等待在同步队列上的线程集合 |
实例
下面代码是独占锁的简单实现
/**
* 通过 AQS 自定义同步组件
*
* @author LBG - 2017/11/8 0008
*/
public class Mutex {
private final Sync sync = new Sync();
public void lock() {
sync.acquire(1);
}
public boolean tryLock() {
return sync.tryAcquire(1);
}
/**
* 限时获取,可中断
*/
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public boolean unlock() {
return sync.release(1);
}
public boolean isLocked() {
return sync.isHeldExclusively();
}
public void lockInterruptible() throws InterruptedException {
sync.acquireInterruptibly(1);
}
/**
* 静态内部类,继承同步器
*/
private class Sync extends AbstractQueuedSynchronizer {
/**
* 当状态为0 时获取锁,并把状态改为1
*/
@Override
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/**
* 释放锁,将状态设为0
*/
@Override
protected boolean tryRelease(int arg) {
if (getState() == 0) {
throw new IllegalMonitorStateException();
}
setExclusiveOwnerThread(null);
setState(0);
return true;
}
/**
* 是否处于独占状态,1 表示独占
*/
@Override
protected boolean isHeldExclusively() {
return getState() ==1;
}
}
}
本篇主要是对 AQS 有个基本的了解,以及主要方法的作用和使用,接下来则按独占式和共享式分别对其源码进行分析。
参考资料:
标签:JUC,同步,AQS,int,编程,boolean,同步器,protected,arg 来源: https://www.cnblogs.com/tailife/p/16356731.html