美文网首页
Java多线程(五)AQS-抽象队列同步器解析

Java多线程(五)AQS-抽象队列同步器解析

作者: GIT提交不上 | 来源:发表于2019-10-13 15:45 被阅读0次

    一、同步器设计

      AQS,全称为AbstractQueuedSynchronizer(抽象队列同步器),是Java多线程显式锁(Lock)的底层实现。同步器的设计属于独占模式:资源是独占的,一次只能一个线程获取。同步器的具体设计方案如下:

    1. 定义一个变量int state=0,变量表示为被获取的资源数量。
    2. 线程在获取资源前先检查state的状态,如果为0,则修改为1,表示获取资源成功,否则表示资源已经被其他线程占用,此时线程要堵塞以等待其他线程释放资源
    3. 为了能使得资源释放后找到那些为了等待资源而堵塞的线程,我们把这些线程保存在FIFO队列中。
    4. 当占有资源的线程释放掉资源后,可以从队列中唤醒一个堵塞的线程,由于此时资源已经释放,因此这个被唤醒的线程可以获取资源并且执行。

    参考链接:JUC解析-AQS(1)

      AQS模型如图1-1所示:

    图1-1 AQS模型.png

    二、AQS成员变量

      AQS底层实现是双向链表的数据结构,通过查看源码,其主要包含的成员变量包括:

    //状态变量state
    private volatile int state;
    //双向链表表头
    private transient volatile Node head;
    //双向链表表尾
    private transient volatile Node tail;
    

      Node类源码如下所示:

    static final class Node {
        //标记一个结点(对应的线程)在共享模式下等待
        static final Node SHARED = new Node();
       // 标记一个结点(对应的线程)在独占模式下等待
        static final Node EXCLUSIVE = null;
    
        //waitStatus的值,表示该结点(对应的线程)已被取消
        static final int CANCELLED =  1;
        //waitStatus的值,表示后继结点(对应的线程)需要被唤醒
        static final int SIGNAL    = -1;
        //waitStatus的值,表示该结点(对应的线程)在等待某一条件
        static final int CONDITION = -2;
        //waitStatus的值,表示有资源可用,新head结点需要继续唤醒后继结点
        static final int PROPAGATE = -3;
    
        volatile int waitStatus;  //等待状态
        volatile Node prev;  //前驱节点
        volatile Node next;  //后继节点
        volatile Thread thread;  //节点对应的线程
        
        Node nextWaiter;  //等待队列下一个等待的节点
        //其余省略
        ... ... ... ...
    }
    

    三、acquire & release资源获取模式

       acquire是一种以独占方式获取资源,如果获取到资源,线程直接返回,否则进入等待队列,直到获取到资源为止,且整个过程忽略中断的影响。acquire方法如下所示:

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

    1.tryAcquire()尝试直接去获取资源,如果成功则直接返回;
    2.addWaiter()将该线程加入等待队列的尾部,并标记为独占模式;
    3.acquireQueued()使线程在等待队列中获取资源,一直获取到资源后才返回。如果在整个等待过程中被中断过,则返回true,否则返回false。
    4.如果线程在等待过程中被中断过,它是不响应的。只是获取资源后才再进行自我中断selfInterrupt(),将中断补上。

      release方法会释放指定量的资源,如果彻底释放了(即state=0),它会唤醒等待队列里的其他线程来获取资源。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;
        }
    

      如果资源释放成功,调用unparkSuccessor(Node)方法唤醒等待队列中下一个线程。这里要注意的是,下一个线程并不一定是当前节点的next节点,而是下一个可以用来唤醒的线程,如果这个节点存在,调用unpark()方法唤醒。unparkSuccessor方法源码如下所示:

    private void unparkSuccessor(Node node) {
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }
    

    参考链接:Java技术之AQS详解
    未完待续

    相关文章

      网友评论

          本文标题:Java多线程(五)AQS-抽象队列同步器解析

          本文链接:https://www.haomeiwen.com/subject/pzjfmctx.html