• AQS 入门


    一 AQS简介

    路径:java.util.concurrent.locks.AbstractOwnableSynchronizer。

    定义:AQS提供了一种 通过维护一个volatile修饰 int类型 state 和 一个FIFO等待队列(双向链表实现)来实现锁功能 的同步器的框架

    描述:队列同步器AQS,抽象类,是用来构建 和synchronized类似效果的锁(或称同步组件)的基础框架,主要使用方法就是继承它,然后实现抽象方法来维护state ,如 ReentrantLock(内部类继承的)就是基于AQS实现的。

    二 核心

    核心方法:同步器提供acquire()和release()两个方法,见名知意,一个方法时获取同步状态,一个是释放,所以这个关键就是操作state。

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

    public final boolean release(int arg) {
            if (tryRelease(arg)) {//同步状态释放成功
                Node h = head;
                if (h != null && h.waitStatus != 0)
                    //直接释放头节点
                    unparkSuccessor(h);
                return true;
            }
            return false;
        }

    主要实现方法:tryAcquire(int arg) 和 tryRelease(int arg)也就是上面的acquire和release中调用的,这里的我们需要完成具体实现。tryAcquire(int arg)就是去获取state,改变他的值,tryRelease(int arg)这里就是释放state。

    AQS类结构:

    主要成员                        图1

    1: state,这是线程之间争夺的资源,也可以称同步状态,改变他的值意味着获取或者释放锁。

    提供3个CAS操作来操作这个同步状态

    • getState()
    • setState()
    • compareAndSetState()

    2:Node head 和Node tail  指向头结点和尾结点,用来操作双向链表的。

    3:内部类Node,双向链表的节点,双向链表实现其实就是Node 里面设置两个 Node的引用

    static final class Node {
      Node next;  下一个节点
      Node pre;   前一个节点 
      Thread thread;  当前线程
      int waitState;   当前node节点的状态,和上面说的同步状态state无关。
      Node nextWaiter;后面说道
    }

    4:内部类ConditionObject,用来操作同步期的等待队列的,就相当于synchronized的等待队列,用的是Object的wait()和notify(),而aqs用的则是ConditionObject对象。

    三:整合

    1:首先调用的线程A会去执行acquire()这个方法,如果tryAcquire()能够获取state(cas改变值),意味着获取到了锁,那么就可以直接执行A的代码了。

    如果失败,会创建一个Node,把当前线程A set其中,链在 tail  尾结点上。接着就是一段自旋去尝试获取锁,直到当前node的pre前一个节点的waitState是指定状态(-1),线程a会被park()这个挂起线程的。这样就不会一直自旋了,解除挂起要么a被中断了,要么前面的节点会release了,会有一个唤醒后面节点的操作unpark。我这里描述的很简单,其实很复杂,要去看源码,参考其他 https://www.cnblogs.com/iou123lg/p/9464385.html

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

    2:一个aqs对象 对应着一个同步队列和多个等待队列,同步队列就是上面说的head和tail组成的,用来存放操作同步状态的 双向链表;而这里我要强调的是等待队列,它是依靠的是ConditionObject这个内部类

    public class ConditionObject implements Condition
    /** First node of condition queue. */
    private transient Node firstWaiter;
    /** Last node of condition queue. */
    private transient Node lastWaiter;

    这里可以看到继承上面的Node的,也就是也有一个nextWaiter字段,等待队列是一个单循环链表firstWaiter和lastWaiter指向头尾,nextWaiter指向下一个,结构就是如此。

    等待队列的节点产生,一个aqs对象能 new 出多个Condition对象,也就意味着能够 有多个等待队列,每个Condition都对应一个等待队列

    例如 condition1=new Condition(),当condition1.await()

    Node node = addConditionWaiter();
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    while (!isOnSyncQueue(node)) {
    LockSupport.park(this);
    if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
    break;
    }

    首先会根据当前线程创建一个Node节点放到condition1的链表中,让lastWaiter和firstWaiter都指向她。

    然后会去释放资源,释放state,做一个减少操作。这里释放资源,意味着同步队列中的头结点要去掉,并且唤醒后面的节点。

    接着 就是一段自循环,判断条件是判断同步队列中是否有这个node,如果没有,则挂起当前线程。如果有则重新去争夺state.正常来说这个新增在等待队列的node是不出现在同步队列中的,所以会挂起,除非调用了signal()方法,相当于Object的notify()

    signal()

    这个方法会去将condition1的等待队列中的firstWaiter 拿出来(也就是删除掉),然后把他再加入同步队列中,也就是从等待队列的头移到了同步队列的尾。

    借鉴:

    https://www.cnblogs.com/iou123lg/p/9464385.html

    https://www.jianshu.com/p/da9d051dcc3d



       

  • 相关阅读:
    初探CSS
    初探CSS -3 语法
    初探 -1 JavaScript
    初探html-17 表单
    初探html-9 链接
    python 发送邮件
    检查xml文件
    python文件和目录操作方法大全(含实例)
    使用ADB无线连接Android真机进行调试
    安卓APP应用在各大应用市场上架方法整理
  • 原文地址:https://www.cnblogs.com/xlblog/p/11575433.html
Copyright © 2020-2023  润新知