zoukankan      html  css  js  c++  java
  • AQS

           AbstractQuenedSynchronizer抽象的队列式同步器。是除了java自带的synchronized关键字之外的锁机制。AQS的全称为(AbstractQueuedSynchronizer),这个抽象类在java.util.concurrent.locks包。

    核心思想(AQS=state+CLH双向队列)

            如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将共享资源设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列变种锁实现的,即将暂时获取不到锁的线程加入到队列中,并且该队列是一个双向队列。也就是说AQS就是基于CLH队列(三个大牛名字的组成,本来是单向的,这里采用的是一个变种),用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁成功,失败则进入等待队列,等待被唤醒。并且AQS是自旋锁,在等待被唤醒时,会不停的自旋操作,直到获取锁成功。(AQS使用一个volatile的int类型的成员变量来表示同步状态,通过内置的FIFO队列来完成资源获取的排队工作将每条要去抢占资源的线程封装成一个Node节点(Node<Thread>)来实现锁的分配,通过CAS完成对State值的修改。)状态标志位state为0时,说明当前资源空闲,可以被当前线程获取。

    AQS

    static final class Node {    //内部类Node
         .
         .
         .
    }
    
    private transient volatile Node head;  //同步器头指针
    
    private transient volatile Node tail;   //同步器尾指针
    
    private volatile int state;      //标志位state

    AQS的内部类Node,Node=waitStatus+前后指针   waitStatus表示当前节点在队列中的状态(volatile的int类型)。

     static final class Node {
           //共享的
            static final Node SHARED = new Node();
          //排他的
            static final Node EXCLUSIVE = null;
    
            //waitStatus的状态
            static final int CANCELLED =  1;     //表示当前线程获取锁的请求已经被取消
            static final int SIGNAL    = -1;     //为-1,表示线程已经准备好了,就等资源释放了
            static final int CONDITION = -2;     //为-2,表示节点在等待队列中,节点线程等待唤醒
            static final int PROPAGATE = -3;     //为-3,当前线程处于SHARED情况下,该字段才能被使用
            
            volatile int waitStatus;
            //前指针
            volatile Node prev;
            //后指针
            volatile Node next;
            //当前Node中的线程,排队的线程
            volatile Thread thread;
    
            Node nextWaiter;

    AQS 定义了两种资源共享方式:
    1.Exclusive:独占,只有一个线程能执行,如ReentrantLock
    2.Share:共享,多个线程可以同时执行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier不同的自定义的同步器争用共享资源的方式也不同。

    以ReentrantLock对AQS分析

         ReentrantLock是重入锁的一种实现,一次只能由一个线程持有锁,实现了Lock接口。其中包含三个内部类:Sync,NonfairSync,FairSync。其锁的实现主要依赖于这三个内部类。

    abstract static class Sync extends AbstractQueuedSynchronizer
    static final class FairSync extends Sync      //公平性锁
    static final class NonfairSync extends Sync    //非公平性锁

    先看看Sync方法,公平锁和非公平锁的父类。 

       abstract static class Sync extends AbstractQueuedSynchronizer {
            private static final long serialVersionUID = -5179523762034025860L;  //序列化
    
            abstract void lock();   //抽象的加锁方法,需要子类来实现
    
            final boolean nonfairTryAcquire(int acquires) {  //非公平和公平性加锁操作的都需要调用的方法
                final Thread current = Thread.currentThread();
                int c = getState(); //获取AQS中state属性值,state = 0:锁空闲,大于0: 锁占用 小于0:锁溢出
                if (c == 0) {  //锁空闲
                    if (compareAndSetState(0, acquires)) {  //通过CAS来确保多线程的安全性,尝试获取锁
                        setExclusiveOwnerThread(current);  //设置当前持有锁的线程
                        return true;  //加锁成功
                    }
                }
    //锁占用(当前线程持有锁或者其他线程持有锁)或者锁溢出
                else if (current == getExclusiveOwnerThread()) {  //判断是否当前持有锁的线程与现在想再次获取锁的线程是否是同一个 (可重入锁的体现,当前获取到锁的线程可以再次获取到锁,并且对state修改)
                    int nextc = c + acquires;   //对持有锁的次数进行变更
                    if (nextc < 0)   //被锁次数上溢(超过int的最大值),
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);   //当前持有锁的线程变更state值
                    return true;      //获取锁成功
                }
                return false;    //当前锁被占用,并且不是当前持有锁的线程想再次获取锁
            }
    
    /**
    *    释放锁调用的方法
    */
            protected final boolean tryRelease(int releases) {
                int c = getState() - releases;   //只能由当前持有锁的线程释放锁
                if (Thread.currentThread() != getExclusiveOwnerThread())
                    throw new IllegalMonitorStateException();
                boolean free = false;
                if (c == 0) {
                    free = true;   //锁被释放
                    setExclusiveOwnerThread(null);  //将持有锁的线程信息置为null
                }
                setState(c);   //变更锁的状态
                return free;  //释放锁操作,当 c==0时,才能真正释放锁,不等于0时,只能变更锁状态,不能真正释放
            }
    
          //释放当前线程持有的锁
            protected final boolean isHeldExclusively() {
                return getExclusiveOwnerThread() == Thread.currentThread();
    
            }
    //获取ConditionObject对象
            final ConditionObject newCondition() {
                return new ConditionObject();
            }
    
    //获取持有锁的线程
            final Thread getOwner() {
                return getState() == 0 ? null : getExclusiveOwnerThread();
            }
    //获取加锁次数
            final int getHoldCount() {
                return isHeldExclusively() ? getState() : 0;
            }
    //判断是否加锁
            final boolean isLocked() {
                return getState() != 0;
            }
    
        }

    Lock有公平性锁和非公平性锁,下面分析调用lock()方法的非公平性锁

    static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
            //lock加锁操作
            final void lock() {
                if (compareAndSetState(0, 1)) //直接通过CAS抢锁,true:抢锁成功(设置AQS里的state量)    (体现一个非公平性,当前线程一来就可以进行CAS尝试抢锁的操作)
                    setExclusiveOwnerThread(Thread.currentThread());//设置锁的持有者
                else
                    acquire(1); //获取锁失败,进入到常规流程,acquire会首先调用tryAcquire (稍后分析该方法)
            }
    
            protected final boolean tryAcquire(int acquires) {
                return nonfairTryAcquire(acquires);
            }
        }

    AQS类的acquire()方法

    public final void acquire(int arg) {
            if (!tryAcquire(arg) &&    //获取锁失败返回false,(注意:取反之后就是true),调用的是上面类中的tyrAcquire()方法,继续进行后续的判断;

    acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //继续调用addWaiter()方法,Node.EXCLUSIVE是一个排他属性 //调用acquireQueued()
     selfInterrupt(); }

    在AQS中的tyrAcquire()方法如下,并么有实际的逻辑代码:(采用了模板模式,子类必须实现该方法,如果么有实现则会抛出异常)。

     protected boolean tryAcquire(int arg) {
            throw new UnsupportedOperationException();   
        }

    真正调用的是非公平锁的类里面的tyrAcquire方法,由该方法调用父类Sync里面的nonfairTryAcquire()方法。(前面有分析)

    AQS类的addWaiter()方法

     private Node addWaiter(Node mode) {
            Node node = new Node(Thread.currentThread(), mode);//当前Node中的线程是抢锁失败的线程
            Node pred = tail;   //当前队列中还么有元素,tail为null,则会进入enq方法
            if (pred != null) {    //当不为空时
                node.prev = pred;    //新节点的前一个节点就是尾结点(尾插法)
                if (compareAndSetTail(pred, node)) {   //设置尾结点
                    pred.next = node;
                    return node;
                }
            }
            enq(node);    //当队列为空时调用该方法
            return node;
        }

    AQS类的enq()方法

    private Node enq(final Node node) {
            for (;;) {
                Node t = tail;
                if (t == null) { //当前的队列中无元素
                    if (compareAndSetHead(new Node()))  //初始化一个头结点(哨兵结点)
                        tail = head;  
                } else {
                    node.prev = t;
                    if (compareAndSetTail(t, node)) {  //设置尾结点
                        t.next = node;                      return t;
                    }
                }
            }
        }

    上面方法的如图分析:

    当队列为空时,死循环第一次进入(t == null),可以看出队列的第一个节点并不是一个有效的节点,而是一个哨兵结点。

    第二次进入,才是第一个有效结点

    AQS的acquireQueued()

     final boolean acquireQueued(final Node node, int arg) {
            boolean failed = true;    //
            try {
                boolean interrupted = false;   //判断是否是可中断的
                for (;;) {
                    final Node p = node.predecessor();   //哨兵结点p
                    if (p == head && tryAcquire(arg)) {    //当前线程再次尝试获取锁
                        setHead(node);
                        p.next = null; // help GC
                        failed = false;
                        return interrupted;
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&   //设置哨兵结点的waitstatues = -1。
                        parkAndCheckInterrupt())      //阻塞线程
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }

    parkAndCheckInterrupt()

     private final boolean parkAndCheckInterrupt() {
            LockSupport.park(this);   //阻塞当前线程,等待被唤醒
            return Thread.interrupted();
        }

    调用unLock()

    public void unlock() {
            sync.release(1);
        }

    AQS的release()方法

      public final boolean release(int arg) {
            if (tryRelease(arg)) {    //调用tryRelease方法,详细见开头Sync的分析
                Node h = head;       //哨兵结点
                if (h != null && h.waitStatus != 0)
                    unparkSuccessor(h);     //
                return true;
            }
            return false;
        }

     

  • 相关阅读:
    ModbusRTU模式和结束符(转)
    modbus字符串的结束符介绍
    IAR平台移植TI OSAL到STC8A8K64S4A12单片机中
    实时系统概念
    单片机的存储区范例
    如何实现返回上一个页面,就像点击浏览器的返回按钮一般
    spring项目中的定时任务实现和问题解决
    context-param与init-param的区别与作用
    Chapter 1 First Sight——16
    一个好用简单的布局空间EasyUI
  • 原文地址:https://www.cnblogs.com/128-cdy/p/13964827.html
Copyright © 2011-2022 走看看