java并发编程的艺术(六)-----AQS

来源:互联网 发布:大数据概念股一览 编辑:程序博客网 时间:2024/06/28 08:54

是什么?

AQS是队列同步器(AbstractQueueSynchronizer),是用来构建锁和完成其他同步组件的基本框架,再lock里面,很多的方法都将用到AQS以获取同步状态,实现锁的语义,而不是依靠传统的synchronized中的对象进行锁获取与释放。

AQS的核心思想

AQS的核心思想是基于volatile int state这样的一个属性同时配合Unsafe工具对其原子性的操作来实现对当前锁的状态进行修改。当state的值为0的时候,标识改Lock不被任何线程所占有

AQS如何解决“羊群效应”

“羊群效应”指的是很多的线程在竞争同一个资源或锁的时候,最后只能由一个线程获得锁,这个获取资源或锁的过程中,就会造成资源的浪费。
AQS采用一个FIFO的同步队列的数据结构,每个节点只需要关心上一个前驱节点的状态,线程唤醒也只唤醒头节点中等待的线程。与其让这些线程相互竞争,还不如它们安排好顺序一个一个来。

AQS的实现

同步队列

之前说的,AQS是采用的一中FIFO队列的数据结构,同步队列的指针里面包含指向头节点和伪结点的指针。而每个节点都是用来保存竞争线程的状态和信息的,节点的主要成员变量如下:

Node {    int waitStatus;    Node prev;    Node next;    Node nextWaiter;    Thread thread;}

独占式同步状态的获取与释放

  • 在获取同步状态的时候,同步器会维护一个同步队列,若获取成功,则直接退出acquire方法;若获取同步状态失败,就会将当前线程的信息添加到同步对列的队尾并在该队列中进行自旋以获得同步状态之后才退出acquire方法。
    这里写图片描述


  • 在释放同步状态的时候有,同步器会调用tryRelease方法释放通过不状态,接着唤醒同步队列中头节点的后继节点。

独占式同步状态的获取与释放源码分析

独占式同步状态的获取源码分析

//通过acquire方法获取同步状态,里面调用由子类实现的tryAcquire方法//若获取失败就将该线程加入同步队列中继续自旋获取同步状态public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();   }//将当前线程的信息构造成节点加入同步队列中//代码执行过程大致就是将当前线程生成的新节点加入同步队列的尾节点//但需要运用compareAndSetTail来确保节点能够被线程安全的添加//因为用普通链表提供的方法来修改,在多线程的并发情况下不能保证节点添加的顺序性private Node addWaiter(Node mode) {        Node node = new Node(Thread.currentThread(), mode);        // Try the fast path of enq; backup to full enq on failure        Node pred = tail;        if (pred != null) {            node.prev = pred;            if (compareAndSetTail(pred, node)) {                pred.next = node;                return node;            }        }        enq(node);       return node;    }    //通过enq这个方法,以一种死循环的方式来保证节点的正确添加,并且是采用CAS操作成功后,enq方法才返回    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;                }            }        }    }//添加新节点到同步节点后,acquireQueued方法就是通过自旋来获取同步状态,//就相当于自旋来竞争锁资源了    final boolean acquireQueued(final Node node, int arg) {        boolean failed = true;        try {            boolean interrupted = false;            for (;;) {                final Node p = node.predecessor();                if (p == head && tryAcquire(arg)) {                    setHead(node);                    p.next = null; // help GC                    failed = false;                    return interrupted;                }                if (shouldParkAfterFailedAcquire(p, node) &&                    parkAndCheckInterrupt())                    interrupted = true;            }        } finally {            if (failed)                cancelAcquire(node);        }    }

独占式同步状态释放的源码分析:

public final boolean release(int arg) {//由具体子类实现同步状态的释放,释放成功后就唤醒头节点的后继节点        if (tryRelease(arg)) {            Node h = head;            if (h != null && h.waitStatus != 0)//这个pravite方法就是用来唤醒后继节点                unparkSuccessor(h);            return true;        }        return false;   }

独占式超时获取同步状态

独占式超时获取同步状态就是在独占式获取同步状态上面添加超时方法返回处理,不必非得等到获取同步状态方法才返回。在源码上面,大致就是acquireQueued和doAcquireNanos的实现过程有差异,在doAcquireNanos里面添加了超时判断和处理代码
这里写图片描述
源码如下:

private boolean doAcquireNanos(int arg, long nanosTimeout)            throws InterruptedException {        if (nanosTimeout <= 0L)            return false;        final long deadline = System.nanoTime() + nanosTimeout;        final Node node = addWaiter(Node.EXCLUSIVE);        boolean failed = true;      //仍然需要自旋来获取同步状态,只不过当剩余时间小于等于0时方法返回,最终取消同步状态的获取        try {            for (;;) {                final Node p = node.predecessor();                if (p == head && tryAcquire(arg)) {                    setHead(node);                    p.next = null; // help GC                    failed = false;                    return true;                }                nanosTimeout = deadline - System.nanoTime();                if (nanosTimeout <= 0L)                    return false;                if (shouldParkAfterFailedAcquire(p, node) &&                    nanosTimeout > spinForTimeoutThreshold)                    LockSupport.parkNanos(this, nanosTimeout);                if (Thread.interrupted())                    throw new InterruptedException();            }        } finally {            if (failed)                cancelAcquir(node);        }    }