zoukankan      html  css  js  c++  java
  • AQS简简单单过一遍

    前言

    回顾前面:

    只有光头才能变强!

    本来我是打算在这章节中写Lock的子类实现的,但看到了AQS的这么一个概念,可以说Lock的子类实现都是基于AQS的

    AQS我在面试题中也见过他的身影,但一直不知道是什么东西。所以本篇我就来讲讲AQS这个玩意吧,至少知道它的概念是什么,对吧~

    那么接下来我们就开始吧~

    一、AQS是什么?

    首先我们来普及一下juc是什么:juc其实就是包的缩写(java.util.concurrnt)

    • 不要被人家唬到了,以为juc是什么一个牛逼的东西。其实指的是包而已~

    我们可以发现lock包下有三个抽象的类:

    • AbstractOwnableSynchronizer
    • AbstractQueuedLongSynchronizer
    • AbstractQueuedSynchronizer

    通常地:AbstractQueuedSynchronizer简称为AQS

    我们Lock之类的两个常见的锁都是基于它来实现的:

    那么我们来看看AbstractQueuedSynchronizer到底是什么,看一个类是干什么的最快途径就是看它的顶部注释

    通读了一遍,可以总结出以下比较关键的信息:

    • AQS其实就是一个可以给我们实现锁的框架
    • 内部实现的关键是:先进先出的队列、state状态
    • 定义了内部类ConditionObject
    • 拥有两种线程模式
      • 独占模式
      • 共享模式
    • 在LOCK包中的相关锁(常用的有ReentrantLock、 ReadWriteLock)都是基于AQS来构建
    • 一般我们叫AQS为同步器

    二、简单看看AQS

    上面也提到了AQS里边最重要的是状态和队列,我们接下来就看看其源码是怎么样的...

    2.1同步状态

    使用volatile修饰实现线程可见性:

    修改state状态值时使用CAS算法来实现:

    2.2先进先出队列

    这个队列被称为:CLH队列(三个名字组成),是一个双向队列

    看看它队列源码的组成:

    
        static final class Node {
         
    		// 共享
            static final Node SHARED = new Node();
          
    		// 独占
            static final Node EXCLUSIVE = null;
    
    		// 线程被取消了
            static final int CANCELLED =  1;
      
    		// 后继线程需要唤醒
            static final int SIGNAL    = -1;
          
    		// 等待condition唤醒
            static final int CONDITION = -2;
          
    		// 共享式同步状态获取将会无条件地传播下去(没看懂)
            static final int PROPAGATE = -3;
    
    		
    		// 初始为0,状态是上面的几种
            volatile int waitStatus;
    
    		// 前置节点
            volatile Node prev;
    
    		// 后继节点
            volatile Node next;
    
    
            volatile Thread thread;
    
           
            Node nextWaiter;
    
            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(Thread thread, Node mode) {     // Used by addWaiter
                this.nextWaiter = mode;
                this.thread = thread;
            }
    
            Node(Thread thread, int waitStatus) { // Used by Condition
                this.waitStatus = waitStatus;
                this.thread = thread;
            }
        }
    
    

    2.3acquire方法

    获取独占锁的过程就是在acquire定义的,该方法用到了模板设计模式,由子类实现的~

    过程:acquire(int)尝试获取资源,如果获取失败,将线程插入等待队列。插入等待队列后,acquire(int)并没有放弃获取资源,而是根据前置节点状态状态判断是否应该继续获取资源,如果前置节点是头结点,继续尝试获取资源,如果前置节点是SIGNAL状态,就中断当前线程,否则继续尝试获取资源。直到当前线程被park()或者获取到资源,acquire(int)结束。

    来源:

    2.4release方法

    释放独占锁的过程就是在acquire定义的,该方法也用到了模板设计模式,由子类实现的~

    过程:首先调用子类的tryRelease()方法释放锁,然后唤醒后继节点,在唤醒的过程中,需要判断后继节点是否满足情况,如果后继节点不为且不是作废状态,则唤醒这个后继节点,否则从tail节点向前寻找合适的节点,如果找到,则唤醒.

    来源:

    三、最后

    总结一下AQS到底是什么吧:

    • juc包中很多可阻塞的类都是基于AQS构建的
      • AQS可以说是一个给予实现同步锁、同步器的一个框架,很多实现类都在它的的基础上构建的
    • 在AQS中实现了对等待队列的默认实现,子类只要重写部分的代码即可实现(大量用到了模板代码)

    有兴趣的同学可去看源码和下面的链接继续学习,我这里就不讲述了。简简单单把AQS过一遍~

    明天就看Lock显式锁实现咯~~~

    参考资料:

    如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y。为了大家方便,刚新建了一下qq群:742919422,大家也可以去交流交流。谢谢支持了!希望能多介绍给其他有需要的朋友

    文章的目录导航

  • 相关阅读:
    成为Java GC专家系列(3) — 如何优化Java垃圾回收机制
    成为JavaGC专家Part II — 如何监控Java垃圾回收机制
    JAVA缓存的实现
    Lock Less Java Object Pool
    跟屌丝大哥学习设计模式--享元模式
    数据库 -- 由数据库连接池引出的三种设计模式
    优秀博客推荐:各种数据结构与算法知识入门经典
    学习总结
    洛谷P3360偷天换日(树形DP)
    疯子的算法总结14--ST算法(区间最值)
  • 原文地址:https://www.cnblogs.com/Java3y/p/8947240.html
Copyright © 2011-2022 走看看