AQS内部采用CLH队列。CLH队列是由节点组成。内部的Node节点包含的状态有

static final int CANCELLED =  1;

static final int SIGNAL    = -1;

static final int CONDITION = -2;

static final int PROPAGATE = -3;

其中取消状态表示任务的取消,SIGNAL状态表示后续节点需要唤醒,CONDITION表示等待状态,PROPAGRATE表示的是传播状态通常用于共享锁的释放。初始节点的状态为0。

AQS中比较重要的操作包括:

public final void acquire(int arg);

public final void acquireInterruptibly(int arg);

public final void acquireShared(int arg);

public final void acquireSharedInterruptibly(int arg);

protected boolean tryAcquire(int arg);

protected int tryAcquireShared(int arg);

public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException;

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException;

其中:public final void acquire(int arg) {
           if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

selfInterrupt();
   }

其中tryAcquire方法为抽象方法。不同的子类有不同的实现方式。AQS中该方法的实现知识抛出了一个异常。

final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;//标记是否成功拿到资源
    try {
        boolean interrupted = false;//标记等待过程中是否被中断过
       
        //自旋的过程
        for (;;) {
            final Node p = node.predecessor();//拿到前驱
            //如果前驱是head,即该结点已成老二,那么便有资格去尝试获取资源(可能是老大释放完资源唤醒自己的,当然也可能被interrupt了)。
            if (p == head && tryAcquire(arg)) {
                setHead(node);//拿到资源后,将head指向该结点。所以head所指的标杆结点,就是当前获取到资源的那个结点或null。
                p.next = null; // setHead中node.prev已置为null,此处再将head.next置为null,就是为了方便GC回收以前的head结点。也就意味着之前拿完资源的结点出队了!
                failed = false;
                return interrupted;//返回等待过程中是否被中断过
            }
           
            //如果自己可以休息了,就进入waiting状态,直到被unpark()
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                interrupted = true;//如果等待过程中被中断过,哪怕只有那么一次,就将interrupted标记为true
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}

可以从该方法中看出。这里会继续尝试去获取一下资源

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;//拿到前驱的状态
    if (ws == Node.SIGNAL)
        //如果已经告诉前驱拿完号后通知自己一下,那就可以安心休息了
        return true;
    if (ws > 0) {
        /*
         * 如果前驱放弃了,那就一直往前找,直到找到最近一个正常等待的状态,并排在它的后边。
         * 注意:那些放弃的结点,由于被自己“加塞”到它们前边,它们相当于形成一个无引用链,稍后就会被保安大叔赶走了(GC回收)!
         */
        do {
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        pred.next = node;
    } else {
         //如果前驱正常,那就把前驱的状态设置成SIGNAL,告诉它拿完号后通知自己一下。有可能失败,人家说不定刚刚释放完呢!
        compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
    }
    return false;
}

在该方法中会检测当前节点中前面的节点是否有CANCELLED状态的如果有。待会儿后续的操作这些节点会被GC回收。

如果一切正常当前节点的前一个节点会被设置为SIGNAL状态。

1 private final boolean parkAndCheckInterrupt() {
2     LockSupport.park(this);//调用park()使线程进入waiting状态
3     return Thread.interrupted();//如果被唤醒,查看自己是不是被中断的。
4 }

线程进入waiting状态。线程被唤醒的方式有被unpark和被中断。

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;//找到头结点
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);//唤醒等待队列里的下一个线程
        return true;
    }
    return false;
}

这里tryRelease也是一个抽象方法不同的子类有不同的实现。

private void unparkSuccessor(Node node) 内部首先会设置当前节点的状态为初始状态。同时找到一个有效的后继节点的状态小于0的进行节点的释放。 LockSupport.unpark(s.thread);//唤醒对应的线程。

java中AQS源码分析的更多相关文章

  1. Java中ArrayList源码分析

    一.简介 ArrayList是一个数组队列,相当于动态数组.每个ArrayList实例都有自己的容量,该容量至少和所存储数据的个数一样大小,在每次添加数据时,它会使用ensureCapacity()保 ...

  2. Java中HashMap源码分析

    一.HashMap概述 HashMap基于哈希表的Map接口的实现.此实现提供所有可选的映射操作,并允许使用null值和null键.(除了不同步和允许使用null之外,HashMap类与Hashtab ...

  3. java中LinkedList源码分析

    ArrayList是动态数组,其实本质就是对数组的操作.那么LinkedList实现原理和ArrayList是完全不一样的.现在就来分析一下ArrayList和LinkeList的优劣吧LinkedL ...

  4. ReentrantLock 与 AQS 源码分析

    ReentrantLock 与 AQS 源码分析 1. 基本结构    重入锁 ReetrantLock,JDK 1.5新增的类,作用与synchronized关键字相当,但比synchronized ...

  5. 并发-AQS源码分析

    AQS源码分析 参考: http://www.cnblogs.com/waterystone/p/4920797.html https://blog.csdn.net/fjse51/article/d ...

  6. 【原】Spark中Client源码分析(二)

    继续前一篇的内容.前一篇内容为: Spark中Client源码分析(一)http://www.cnblogs.com/yourarebest/p/5313006.html DriverClient中的 ...

  7. 【JAVA】ThreadLocal源码分析

    ThreadLocal内部是用一张哈希表来存储: static class ThreadLocalMap { static class Entry extends WeakReference<T ...

  8. 【Java】HashMap源码分析——常用方法详解

    上一篇介绍了HashMap的基本概念,这一篇着重介绍HasHMap中的一些常用方法:put()get()**resize()** 首先介绍resize()这个方法,在我看来这是HashMap中一个非常 ...

  9. 【Java】HashMap源码分析——基本概念

    在JDK1.8后,对HashMap源码进行了更改,引入了红黑树.在这之前,HashMap实际上就是就是数组+链表的结构,由于HashMap是一张哈希表,其会产生哈希冲突,为了解决哈希冲突,HashMa ...

随机推荐

  1. control+Z的逆 control+Y

    接触过电脑的朋友一定知道control键加Z可以在大多时候撤销我们前一步的操作,相当于计算机系统里最“广谱”的后悔药. 然而,你有没有在编辑文本的时候却因为撤销的操作而后悔?输入文本之后撤销,发现少了 ...

  2. cb16a_c++_顺序容器的选用_排序_二分查找

    /*cb16a_c++_顺序容器的选用_排序_二分查找顺序容器: 1.vector的优点与缺点 vector优点:排序利用下标,快速排序,做二分查找非常快 2.list的优点与缺点 list优点:插入 ...

  3. vue 生命周期:

    vue  生命周期: 1. beforeCreate()创建组件;        2. created() 创建完成;        3. beforeMounte() 组件被挂裁前;        ...

  4. sklearn机器学习算法--线性模型

    线性模型 用于回归的线性模型 线性回归(普通最小二乘法) 岭回归 lasso 用于分类的线性模型 用于多分类的线性模型 1.线性回归 LinearRegression,模型简单,不同调节参数 #2.导 ...

  5. base64格式的图片上传阿里云

    base64格式的图片上传阿里云 上传图片的时候,除了普通的图片上传,还有一张图片信息是以base64格式发送到后台的. 后台接受base64格式的图片,上传至阿里云代码:(主要是将base64转化成 ...

  6. nova api报错network问题

    安装openstack Rocky版本的时候,在未安装网络服务前,创建虚拟机,报以下错误 [root@controller2 nova]# openstack server create --flav ...

  7. 【Python3爬虫】模拟实现小牛在线登录过程

    一.站点分析 小牛在线的登录入口地址为:https://www.xiaoniu88.com/user/login. 用户登录时除了需要输入用户名和密码,还要输一个验证码.我们可以先任意输入一个用户名. ...

  8. 《算法笔记》6.7小节 问题 A: 简单计算器

    又是大模拟淦淦淦淦淦淦淦 思路: 这道题我居然用的队列orz. 言归正传,这道题就是模拟计算器.从读题目样例可以发现,数字的个数只比符号的个数多一个,那么这就给我们了思路:用队列,先提前放出一个数,每 ...

  9. Spring Security(一) —— Architecture Overview

    摘要: 原创出处 https://www.cnkirito.moe/spring-security-1/ 「老徐」欢迎转载,保留摘要,谢谢! 1 核心组件 一直以来我都想写一写Spring Secur ...

  10. Python-数据结构-最全六种排序代码实现

    1.冒泡排序 def bubble_sort(alist): """冒泡排序""" n = len(alist) for j in rang ...