多线程 -- AQS

简述:AQS(AbstractQueuedSynchronizer)抽象的队列同步器,其是 JUC 包众多锁机制和信号量机制的基础,例如 ReentrantLock、Semaphore、CountDownLatch、ReadWriteLock、CyclicBarrier 底层的同步互斥操作都建立在 AQS 之上。AQS 的本质是一个采用双向链表实现的 FIFO 线程等待队列,等待队列上的线程释放和等待都依据一个整型的信号量 volatile int state ,可以将这个信号量视为各个线程所竞争的 "资源"。如下图所示

 

AQS定义了一系列操作这个等待队列的方法规范,需要定制化采用 AQS 来实现同步器亦或是自定义规则的锁,可以先继承 AQS 这个类,然后重写它对于 state 变量的一系列操作(模板方法模式)。这些你可以在 ReentrantLock 的源码里面一探究竟。

 

资源共享方式:AQS 提供了两种资源共享的方式,即独占(Exclusive)和共享(Share),独占的例子有 ReentrantLock,共享的例子有 CountDownLatch、ReadWriteLock。

 

学习简述:

1、首先线程进入队列等待需要 AQS 配套实现如何将线程挂起和唤醒,底层用到的是 LockSupport 工具类,里面提供了 pack() 和 unpack() 两个 native 方法来进行线程的挂起和唤醒

2、等待队列的 Node 都包含了什么信息?其主要的当然包含了线程信息、线程状态信息、节点前驱后继等,强烈建议看源码(英文注释十分之好)

static final class Node {
        static final Node SHARED = new Node();//表示是共享模式,源码里面的nextWaiter字段有注释说明了这个 special value
        static final Node EXCLUSIVE = null;//表示是独占模式
        static final int CANCELLED =  1;
        static final int SIGNAL    = -1;
        static final int CONDITION = -2;
        static final int PROPAGATE = -3;
        volatile int waitStatus;
        volatile Node prev; //前驱节点
        volatile Node next; //后继节点
        volatile Thread thread;//当前线程
        Node nextWaiter; //存储在condition队列中的后继节点
        //是否为共享锁
        final boolean isShared() { 
            return nextWaiter == SHARED;
        }

        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }
        //将线程构造成一个Node,添加到等待队列
        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }
        //这个方法会在Condition队列使用,后续单独写一篇文章分析condition
        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }
AQS 中的 Node

3、除了同步队列,在独占模式下,还可以使用 Condition 来让线程进入条件队列等待。说白了,就是你可以视 AQS 里面有两个队列,一个是同步队列,里面的线程等待调度获取资源,而条件队列是针对某一条件来让线程进行等待唤醒。当位于条件队列的线程被唤醒之后会被转化到同步队列中去,源码里面有个方法 transferForSignal 就是干这个事情的!而 AQS 中的 Node 为两用,同时可作为同步队列的节点也可以作为条件队列的节点,可以看源码说明。

4、AQS 是个模板方法模式的经典体现,定义了一些类规则方法或者说接口,让这些具体的方法延续到子类(即各种锁或者信号量)去实现,那么具体是怎么进行实现和操作的可以看别人的博客,但是更重要的是,博客的说明一定要自己进去源码一探究竟,这样就有更深的体会

 

下面参考链接:

AQS 源码中各种方法详解

AQS 源码中各种方法详解2

并发数据结构的基石

 AQS 简单应用--自定义 mutex 互斥锁

ReentrantLock与AQS

Lock与AQS

猜你喜欢

转载自www.cnblogs.com/qwertiLH/p/12688249.html