AbstractQueuedSynchronizer 源码分析

综述

AbstractQueuedSynchronizer 是java 多线程阻塞库和同步器实现的基础类。以下简称为AQS。AQS 底层数据结构是一个FIFO的双端队列。实现依赖单一原子状态(int state)state,state的更新操作必须是原子的,子类必须Overload protected方法改变状态,通过状态值可以实现获取锁和释放锁的定义。此类支持排它锁和共享锁。在互斥锁下,其他的线程不能成功的获取锁。在共享下,多个线程可以获取锁但不一定成功。共享锁和互斥锁的区别是,在共享锁下,当一个线程获取锁成功后,其他的等待线程也可以尝试获取锁。共享锁和互斥锁在实现上使用同一个FIFO队列。

为了使用这个类,必须实现以下几个方法:这些方法的默认实现抛出UnsupportedOperationException
1. tryAcquire
2. tryRelease
3. tryAcquireShared
4. tryReleaseShared
5. isHeldExclusively

AQS实现可以支持两种锁模式:共享锁和互斥锁
总结:当一个线程加入 CHL队列时,如果不是头结点需要判断该节点是否需要挂起;在释放锁时,需要唤醒该节点的后继节点

Acquire和Release实现模式

以下是互斥模式的实现:
Acquire:
while (!tryAcquire(arg)) {
如果当前线程不在队列就压入队列;
可能阻塞当前线程;
}
Release:
if (tryRelease(arg))
释放队列头部的节点;
共享模式是类似的,但涉及到唤醒它的后继节点。

等待队列节点类

等待队列节点是一个双端队列。需要等待的线程加入到队列的尾部,线程释放从队列的头部开始。线程的出队和入队的操作必须是原子性的。队列结构图如下:

这里写图片描述

队列中的pre主要为了处理被取消的线程。如果一个线程被取消,它的后继者必须重新连接到一个不是被取消的节点。
使用next节点实现阻塞机制,每个node节点都包含自己的线程Id,
Header节点是一个哨兵节点,实际不包含线程。
线程的挂起与调度通过工具类LockSupport实现,分别是park()和uppark方法。

static final class Node {
/* 表示一个线程在共享锁下正在等待/
static final Node SHARED = new Node();
/* 表示一个线程在排它锁下正在等待 /
static final Node EXCLUSIVE = null;

    /** 如果waitStatus==1表示此线程被取消*/
    static final int CANCELLED =  1;
    /**如果waitStatus==-1表示后继线程需要被唤醒 */
    static final int SIGNAL    = -1;
    /** 如果waitStatus==-2表示此线程在等待条件 */
    static final int CONDITION = -2;
    /**
     * 如果waitStatus==-3表示下一个acquireShared应该被无条件的传播
     */
    static final int PROPAGATE = -3;
    //waitStatus的默认值表示为0,
    volatile int waitStatus;

####node源码类

   static final class Node {
        /** 表示一个线程在共享锁下正在等待*/
        static final Node SHARED = new Node();
        /** 表示一个线程在排它锁下正在等待 */
        static final Node EXCLUSIVE = null;

        /** 如果waitStatus==1表示此线程被取消*/
        static final int CANCELLED =  1;
        /**如果waitStatus==-1表示后继线程需要被唤醒 */
        static final int SIGNAL    = -1;
        /** 如果waitStatus==-2表示此线程在等待条件 */
        static final int CONDITION = -2;
        /**
         * 如果waitStatus==-3表示下一个acquireShared应该被无条件的传播
         */
        static final int PROPAGATE = -3;
        //waitStatus的默认值表示为0,
        volatile int waitStatus;

prev 节点说明

  /**
   * 当前节点或者线程的前节点。当一个前继节点被取消时,我
   * 们可以总是发现一个没有被取消的节点作为前继节点,因为
   * 一个head节点绝不会被取消,当一个节点变成head节点,
   * 只有当此节点成功的获取锁。一个被取消的线程在获取锁时
   * 绝不能成功。一个线程只能取消自己。
  */
 volatile Node prev;
 //队列头节点,挂起线程的唤醒在队列头部进行
 private transient volatile Node head;
 //队列尾节点,没有获取锁的线程加入到队列尾部
 private transient volatile Node tail;
 //同步状态
 private volatile int state;

UnSafe compareAndSetXXX(expect,update),如果当前值和期望值相等,则更新成update值。

  protected final boolean compareAndSetState(int expect, int update) {
        //stateOffset 字段state内存地址偏移量,为了获取当前
        //state的值
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

循环入队实现
队列入队时把节点加入到队列尾部,因为有别的线程可能也在进行入队操作,因此循环操作实现队列入队。
如果:tail == null,创建一个node,并让tail和heand同时指向此node。
如果 tail !=null,设置node.pre=tail,tail = node

 private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // Must initialize
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

线程加入等待队列实现:
如果队列尾节点不为空,直接尝试加入到尾部,如果失败,则调用enq(node)方法,循环入队。

  private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        //首先尝试直接把此节点加入到队列尾部,如果失败,则调用
        //enq(node)循环,直到加入成功。
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            //原子的更新尾节点
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        //调用入队操作
        enq(node);
        return node;
    }

唤醒后继节点,如果存在。
找到下一个节点,如果为null后者被取消,则从尾节点反向遍历找到第一个需要被唤醒的节点

   private void unparkSuccessor(Node node) {
        /*
         * 由于需要唤醒后继节点的线程释放了锁。
         * waitStatus=SINGAL
         * 原子的设置waitStatus=0
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * 找到下一个节点,如果为null后者被取消,则从尾节点反
         * 向遍历找到第一个需要被唤醒的节点。
         */
        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);
    }

释放共享锁时唤醒后继节点

   /**
     * 释放共享锁。如果当前节点waitStatus = signal,则唤醒后
     * 继节点。如果waitStatus=0,则设置waitStatus=progagate
     * 
     */
    private void doReleaseShared() {
        /*
         * 考虑有多个线程操作的情况。循环调用
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;  
                    //唤醒后继节点           
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue; //fail 继续
            }
            if (h == head) //保证head没改变                
                break;
        }
    }

获取共享锁代码实现

   private void doAcquireShared(int arg) {
        //添加到等待队列
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                /*
                 *获取此节点的前继节点。
                 *如果前继节点==head节点。则获取锁
                 *
                 */
                final Node p = node.predecessor();
                if (p == head) {
                    //尝试获取锁,如果成功则返回大于0
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        //设置队列头结点并唤醒唤醒后继节点
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //判断在获取锁失败后设置是否需要唤醒后继节点
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值