【JUC 并发编程】— AQS 概述与实例

2022/6/9 1:21:34

本文主要是介绍【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 getQueuedThreads() 获取等待在同步队列上的线程集合

实例

下面代码是独占锁的简单实现

/**
 * 通过 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 有个基本的了解,以及主要方法的作用和使用,接下来则按独占式和共享式分别对其源码进行分析。


参考资料:

  • Java并发编程实战
  • Java并发编程的艺术
  • 深入理解Java虚拟机(第2版)


这篇关于【JUC 并发编程】— AQS 概述与实例的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程