【Java并发】AQS四:AbstractQueuedSynchronizer内部方法分类详细

AbstractQueuedSynchronizer作为一个同步的基础框架,里面封装了很多的方法,通过对state值的定义处理,以及排队机制来实现同步机制。

一:state值定义

核心state状态值,通过对其的设置获取来判断是否有资格进入同步锁定资源

	/**
     * 等待队列的头,延迟初始化。除了初始化之外,它只通过setHead方法进行修改。注意:如果head存在,则保证它的等待状态不会被取消。
     */
    private transient volatile Node head;
    /**
     * 等待队列的尾部,延迟初始化。仅通过方法enq()修改以添加新的等待节点。
     */
    private transient volatile Node tail;
    /**
     * 同步状态。核心部分,根据其值来判定同步状态
     */
    private volatile int state;
    /**
     * 返回同步状态的当前值。该操作具有{@code volatile}读取的内存语义,即内存可见性、禁止重排序
     */
    protected final int getState() {
        return state;
    }
    /**
     * 设置同步状态的值。这个操作具有{@code volatile}写的内存语义。
     */
    protected final void setState(int newState) {
        state = newState;
    }
    /**
     * 如果当前状态值等于期望值,则自动将同步状态设置为给定的更新值。该操作具有{@code volatile}读写的内存语义。
     */
    protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

二:内部排队公用方法

内部排队公用方法,供AQS内部之间使用

	// 自旋机制提供的自旋纳秒数,提高响应能力
    static final long spinForTimeoutThreshold = 1000L;
    // 往队列里面加入一个节点
    private Node enq(final Node node)
    // 根据给定模式(共享/排他)创建或加入队列
    private Node addWaiter(Node mode)
    // 设置头节点
    private void setHead(Node node)
    // 唤醒继承节点线程
    private void unparkSuccessor(Node node)
    // 共享模型下是否共享值
    private void doReleaseShared()
    // 设置队列的头部,并检查后续队列是否可以在共享模式下等待,如果设置了propagate > 0或propagate status,则将进行传播。
    private void setHeadAndPropagate(Node node, int propagate)

三:各种版本获取的实用程序

// 取消尝试 acquire 操作
private void cancelAcquire(Node node)
// 检查和更新未能获取的节点的状态。如果线程阻塞,返回true。这是所有采集回路的主要信号控制。需要pred == node.prev。
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node)
// 中断当前线程方法
static void selfInterrupt()
// 阻塞当前线程,然后检查当前线程是否中断
private final boolean parkAndCheckInterrupt()

获取的不同风格,在独占/共享和控制模式中有所不同。每一种都大同小异,但却令人讨厌地不同。由于异常机制(包括确保我们在尝试获取抛出异常时取消)和其他控件之间的交互,只有少量的因式分解是可能的,至少不会对性能造成太大影响。

// 获取队列中已存在线程的独占不可中断模式。用于条件等待方法以及获取。
final boolean acquireQueued(final Node node, int arg)
// 以独占中断模式获取。
private void doAcquireInterruptibly(int arg)
// 以独占时间模式获取。
private boolean doAcquireNanos(int arg, long nanosTimeout)
// 以共享不可中断模式获取。
private void doAcquireShared(int arg)
// 以共享中断模式获取。
private void doAcquireSharedInterruptibly(int arg)
// 以共享时间模式获取。
private boolean doAcquireSharedNanos(int arg, long nanosTimeout)

四:对外公开方法

此类方法用于 子类在实现该AQS基础框架时,需要根据自定义的共享或排他模式,进行实现的公开对外方法。

首先,供子类继承AQS自定义实现同步机制(共享/排他)的方法,总共就5个方法:

// 试图以独占模式获取。这个方法应该查询对象的状态是否允许以独占模式获取它,如果允许,也应该查询是否允许以独占模式获取它。
// 执行获取的线程总是调用此方法。如果此方法报告失败,如果尚未排队,则获取方法可以对线程进行排队,直到从其他线程发出释放信号。
// 这可以用来实现方法{@link Lock#tryLock()}。
// 参数 arg 这个值总是传递给获取方法的值,或者是在进入条件wait时保存的值
// 如果获取将使此同步器处于非法状态。必须以一致的方式抛出此异常,以便同步工作正常。
protected boolean tryAcquire(int arg)
// 试图将状态设置为以独占模式反映发布。此方法始终由执行发布的线程调用。
protected boolean tryRelease(int arg)
// 共享模式下获得状态值
protected int tryAcquireShared(int arg)
// 共享模式下释放状态值
protected boolean tryReleaseShared(int arg)
// 如果同步仅针对当前(调用)线程执行,则返回{@code true}。此方法在每次调用非等待的{@link ConditionObject}方法时调用。
// (相反,等待方法调用{@link #release}。) 此方法仅在{@link ConditionObject}方法内部调用,因此如果不使用条件,则不需要定义该方法。
protected boolean isHeldExclusively()

其次,实现AQS的子类,在使用时,供外部使用的方法:

// 以独占模式获取,忽略中断。通过至少调用一次{@link #tryAcquire}来实现,成功后返回。否则,线程将排队,
// 可能会反复阻塞和解除阻塞,调用{@link #tryAcquire}直到成功。此方法可用于实现方法{@link Lock# Lock}。
public final void acquire(int arg)
// 以独占模式获取,如果中断将中止。首先检查中断状态,然后至少调用一次{@link #tryAcquire},成功后返回。否则,
// 线程将排队,可能反复阻塞和解除阻塞,调用{@link #tryAcquire},直到成功或线程被中断。此方法可用于实现方法{@link Lock#lockInterruptibly}。
public final void acquireInterruptibly(int arg)
// 在指定时间内,一直获取,排他模式
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
// 以独占模式发布。如果{@link #tryRelease}返回true,则通过解阻塞一个或多个线程来实现。此方法可用于实现方法{@link Lock#unlock}。
public final boolean release(int arg)
// 共享模式下获取
public final void acquireShared(int arg)
// 以共享模式获取,如果中断将中止。首先检查中断状态,然后至少调用一次{@link # tryacquiremred},成功后返回。
// 否则,线程将排队,可能反复阻塞和解除阻塞,调用{@link # tryacquiremred},直到成功或线程被中断。
public final void acquireSharedInterruptibly(int arg)
// 在指定时间内,一直获取,共享模式
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
// 释放共享锁
public final boolean releaseShared(int arg)

跟着acquire()的源码:

public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
  1. tryAcquire(arg) : 需要用于自定义同步机制集成AQS实现的类,主要实现思路是判断state状态是否可用,可用就获取它,并且state值加arg
  2. addWaiter(Node.EXCLUSIVE) :创建排他模式队列,并把当前线程作为节点加入到队列尾部。
  3. acquireQueued(node, arg) :
    先获得同步资源,如果获取同步资源失败,则通过自旋锁自旋转获取前驱线程节点,并继续尝试获得同步资源直到前驱节点为头节点,且获取同步资源成功,
    则设置当前线程node节点为头节点,跳出自旋锁返回,如果整个过程发生中断返回true,否则返回false
  4. selfInterrupt(); 当获取失败时,且自旋获取期间发生中断,进行一次自我中断

查看release()源码:

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}

1、tryRelease(arg):需要自定义同步机制继承AQS时,实现的方法,通过释放资源值,即state - args
2、unparkSuccessor(h):唤醒头节点的后续节点

五:队列检查方法

// 查询是否有线程正在等待获取。注意,由于中断和超时导致的取消可能随时发生,返回true并不保证任何其他线程将获得。
public final boolean hasQueuedThreads()
// 查询是否有线程争用过此同步器;也就是说是否获取方法曾经被阻塞。
public final boolean hasContended()
// 返回队列中的第一个(等待时间最长的)线程,如果当前没有线程排队,则返回{@code null}
// 在此实现中,此操作通常以常数时间返回,但如果其他线程同时修改队列,则可能在争用时迭代。
public final Thread getFirstQueuedThread()
// 判断给定的线程是否在排队
public final boolean isQueued(Thread thread)
// 如果第一个排队的线程(如果存在)正在以排它模式等待,则返回{@code true}。如果这个方法返回{@code true},并且当
// 前线程正在尝试以共享模式获取(也就是说,这个方法是从{@link # tryacquiremred}调用的),那么可以保证当前线程不是第
// 一个排队的线程。仅在ReentrantReadWriteLock中作为启发式使用。
final boolean apparentlyFirstQueuedIsExclusive()
// 查询是否有任何线程等待获取的时间超过当前线程。该方法是设计用于一个公平的同步器.
public final boolean hasQueuedPredecessors()

六:测量和监控队列的方法

// 返回等待获取的线程数的估计值。这个值只是一个估计值,因为当这个方法遍历内部数据结构时,线程的数量可能会动态变化。该方法用于监控系统状态,不用于同步控制。
public final int getQueueLength() 
// 返回一个集合,其中包含可能正在等待获取的线程,仅为预估值
public final Collection getQueuedThreads()
// 和getQueuedThreads一样,但是这个方法返回的只是独占模式下排队线程数
public final Collection getExclusiveQueuedThreads()
// 和getQueuedThreads一样,但是这个方法返回的只是共享模式下排队线程数
public final Collection getSharedQueuedThreads()

七:条件的内部支持方法

// 如果一个节点(始终是最初放置在条件队列中的节点)现在正等待在同步队列上重新获取,则返回true。
final boolean isOnSyncQueue(Node node)\
// 如果节点位于同步队列上,则通过从tail向后搜索返回true。仅在isOnSyncQueue需要时调用。
private boolean findNodeFromTail(Node node)
// 将节点从条件队列传输到同步队列。如果成功返回true。
final boolean transferForSignal(Node node)
// 如果需要,在取消等待后传输节点来同步队列。如果线程在发出信号之前被取消,则返回true。
final boolean transferAfterCancelledWait(Node node)
// 使用当前状态值调用release;返回保存的状态。取消节点并在失败时抛出异常。
final int fullyRelease(Node node)

八:条件测量方法

// 查询给定条件对象是否使用此同步器作为其锁。
public final boolean owns(ConditionObject condition)
// 查询是否有线程正在等待与此同步器关联的给定条件。注意,
// 由于超时和中断可能随时发生,{@code true}返回并不保证将来的{@code信号}将唤醒任何线程。该方法主要用于系统状态监测。
public final boolean hasWaiters(ConditionObject condition)
// 返回等待的线程数的估计值,该方法适用于对某型水电站的监测系统状态,不用于同步控制。
public final int getWaitQueueLength(ConditionObject condition)
// 根据条件获取等待的线程
public final Collection getWaitingThreads(ConditionObject condition) 
//  一些Unsafe底层方法操作,保证CAS的正确性
private static final Unsafe unsafe = Unsafe.getUnsafe();
....
....
....

总结:比较常用的是第四类方法,对外公开方法,形如ReentrantLock,Semaphore,CountDownLatch都是实现此框架,里的对外公开方法。当然根据AQS我们也可以定义属于我们自己的同步机制

你可能感兴趣的:(java并发)