Java并发编程(七)队列同步器AQS

一、AQS简介

队列同步器AbstractQueuedSynchronizer(简称AQS)是用来构建锁或其他同步组件的基础框架,它服务的是锁的实现者。AQS有一个变量表示同步状态,通过内置的FIFO管理线程排队,基于AQS可以将同步状态管理、线程排队、等待与唤醒等操作对锁屏蔽,简化锁的实现方式。

同步器的设计是基于模板方法的,使用者需要重写同步器指定的方法,然后将同步器组合在自定义同步组件的视线中,并调用同步器提供的模板方法。AQS的模板方法包括获取和释放同步状态等。

二、AQS原理

理解AQS主要是理解同步队列管理同步状态的获取与释放两点。

1、队列同步器结构

AQS有两个节点的引用head和tail,分别指向头尾节点。

入队设置尾节点:获取同步状态(或者说获取锁)失败的线程,会被作为节点加入队列,为保证加入过程的线程安全,通过compareAndSetTail方式入队。(第一次设置头结点是在这个操作中)

出队设置头结点:由于只有一个线程能够成功获取到同步状态,因此设置头结点不需要CAS保证,只需将头节点的下一个节点设为头结点。

2、同步状态的获取与释放

获取过程调用方法acquire:

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

获取成功时做两件事:一是更新同步状态+1,二是setExclusiveOwnerThread设置获得同步状态的线程为当前线程。

获取逻辑:通过调用同步器方法tryAcquire方法,尝试获取同步状态,如果获取失败,则构造同步节点,并CAS调用addWaiter方法尝试将节点加入同步队列尾部。同步队列中的节点线程调用acquireQueued方法,以死循环的方式获取同步状态。如果获取不到同步状态,则阻塞节点中的线程,唤醒线程的方式是获取同步状态成功或线程被中断。获取过程如下图:

释放过程调用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,二是setExclusiveOwnerThread(null)设置获得同步状态的线程为null。

释放逻辑:尝试释放,如果成功则唤醒后继节点线程。

3、获取同步状态的方式

获取同步状态的方式有3种:独占式获取同步状态、共享式获取同步状态太、独占式超时获取同步状态。

三者的区别在于,独占式同一时刻只会有一个线程获得到同步状态,而共享式可以有多个线程获得同步状态。独占式超时获取同步状态,如果获取不成功,在超时后会放弃而不会一直阻塞(这也是Lock相对于synchronized增加的功能)。

 

参考资料:

《Java并发编程的艺术》

猜你喜欢

转载自blog.csdn.net/ss1300460973/article/details/84753862
今日推荐