java并发编程——九 AbstractQueuedSynchronizer AQS详解

来源:互联网 发布:第三方支付数据统计 编辑:程序博客网 时间:2024/06/01 14:58

AbstractQueuedSynchronizer概述

AQS用来实现锁或其他同步组件的基础框架(注意区别synchronized是在字节码上加指令方式,通过底层机器语言保证同步),AQS使用int类型的volatile变量维护同步状态,使用Node实现FIFO队列来完成Task的排队执行。在锁的实现中通过组合AQS对象的方式使用,利用AQS实现锁的语义。

  • AQS与锁(如Lock)的对比:

锁是面向使用者的,锁定义了用户调用的接口,隐藏了实现细节;

AQS是锁的实现者,通过用AQS简化了锁的实现屏蔽了同步状态管理,线程的排队,等待唤醒的底层操作。

简而言之,锁是面向使用者,AQS是锁的具体实现者。


AbstractQueuedSynchronizer的使用

abstract AQS的设计基于模版方法,使用者继承这个abstract AQS,并重写其中的方法。AQS提供了如下final方法,与同步状态交互。
- getState() 获取当前同步状态

    protected final int getState() {        return state;    }
  • setState()设置当前同步状态
    protected final void setState(int newState) {        state = newState;    }
  • compareAndSetState(int expect,int update) 调用unsafe底层C语言,保证原子性的改变同步状态。
    protected final boolean compareAndSetState(int expect, int update) {        // See below for intrinsics setup to support this        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);    }

AQS中可选择重写的方法如下:

  • tryAcquire:独占式获取同步状态
    protected boolean tryAcquire(int arg) {        throw new UnsupportedOperationException();    }
  • tryRelease:独占式释放同步状态
    protected boolean tryRelease(int arg) {        throw new UnsupportedOperationException();    }
  • tryAcquireShared :共享式获取同步状态,返回值>=0表示成功,否则失败。
    protected int tryAcquireShared(int arg) {        throw new UnsupportedOperationException();    }

tryReleaseShared:共享式释放同步状态

    protected boolean tryReleaseShared(int arg) {        throw new UnsupportedOperationException();    }
  • isHeldExclusively: AQS是否被当前线程独占
    protected boolean isHeldExclusively() {        throw new UnsupportedOperationException();    }

AQS提供的其他的模版方法供子类实现者调用,主要分为三类:
1独占式获取/释放同步状态的模版方法 ;
2共享式获取/释放同步状态的模版方法;
3同步队列中等待线程查看
具体如下:


独占式获取同步状态的模版方法:

  • acquire(int arg) :独占式获取同步状态,如果获取失败则进入等待队列。
    public final void acquire(int arg) {        if (!tryAcquire(arg) &&            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();    }
  • acquireInterruptibly:与acquire(int arg) 类似,但该方法可相应中断(抛异常)。
    public final void acquireInterruptibly(int arg) throws InterruptedException {        if (Thread.interrupted())            throw new InterruptedException();        if (!tryAcquire(arg))            doAcquireInterruptibly(arg);    }
  • tryAcquireNanos :在tryAcquire()基础上增加了超时限制,如果超时则会中断等待返回false。
    public final boolean tryAcquireNanos(int arg, long nanosTimeout) throws InterruptedException {    if (Thread.interrupted())        throw new InterruptedException();    return tryAcquire(arg) ||        doAcquireNanos(arg, nanosTimeout);    }

共享式获取同步状态的模版方法:

  • acquireShared(int arg):与 acquire(int arg)的类似,区别在于共享式获取同步状态,同一时刻允许多个线程获取同步状态。
    public final void acquireShared(int arg) {        if (tryAcquireShared(arg) < 0)            doAcquireShared(arg);    }
  • acquireSharedInterruptibly:在acquireShared基础上加入了相应中断实现
    public final void acquireSharedInterruptibly(int arg) throws InterruptedException {        if (Thread.interrupted())            throw new InterruptedException();        if (tryAcquireShared(arg) < 0)            doAcquireSharedInterruptibly(arg);    }
  • tryAcquireSharedNanos:在acquireShared基础上增加超时限制;
    public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {    if (Thread.interrupted())        throw new InterruptedException();    return tryAcquireShared(arg) >= 0 ||        doAcquireSharedNanos(arg, nanosTimeout);    }

独占式释放同步状态的模版方法:

  • release(int arg):独占式释放同步状态
    public final boolean release(int arg) {        if (tryRelease(arg)) {            Node h = head;            if (h != null && h.waitStatus != 0)                unparkSuccessor(h);            return true;        }        return false;    }

共享式释放同步状态的模版方法:

  • acquireShared: 共享式释放同步状态
    public final void acquireShared(int arg) {        if (tryAcquireShared(arg) < 0)            doAcquireShared(arg);    }

同步队列中等待线程查看

  • getQueuedThreads: Returns a collection containing threads that may be waiting to acquire.返回队列中等待的线程集合。
    public final Collection<Thread> getQueuedThreads() {        ArrayList<Thread> list = new ArrayList<Thread>();        for (Node p = tail; p != null; p = p.prev) {            Thread t = p.thread;            if (t != null)                list.add(t);        }        return list;    }

AQS使用实例一:

package com.aqs.test;import java.util.concurrent.TimeUnit;import java.util.concurrent.locks.AbstractQueuedSynchronizer;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;/** *  *  * @author zs * *         排它锁 */public class MutexLock implements Lock {    // 通常使用静态内部类,实现自定义同步器    private static class Sync extends AbstractQueuedSynchronizer {        /**         * 当前线程是否独占这个锁         */        protected boolean isHeldExclusively() {            return getState() == 1;        }        /**         * 获取锁         */        protected boolean tryAcquire() {            if (compareAndSetState(0, 1)) {                setExclusiveOwnerThread(Thread.currentThread());                return true;            }            return false;        }        /**         * 释放锁         */        protected boolean tryRelease() {            if (getState() == 0) {                throw new IllegalMonitorStateException("锁未被当前线程占用");            }            setExclusiveOwnerThread(null);// 置为null表示锁未被任何线程占用            setState(0);            return true;        }        /**         * 返回一个Condition,类似Lock实现中的Condition:await()&& signal()&&signalAll()         *          * @return         */        protected Condition newCondition() {            return new ConditionObject();        }    }    // Sync 其实就是个AQS(继承关系),这个Sync对象为使用者屏蔽了锁的实现,    // 使用者只需要通过组合使用这个sync来实现锁的使用;    private final Sync sync = new Sync();    @Override    public void lock() {        sync.acquire(1);// AQS独占式获取锁的模版方法    }    @Override    public void lockInterruptibly() throws InterruptedException {        sync.acquireInterruptibly(1);// AQS独占式可响应中断 获取锁的模版方法    }    @Override    public boolean tryLock() {        return sync.tryAcquire();    }    @Override    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {        return sync.tryAcquireNanos(1, unit.toNanos(time));    }    @Override    public void unlock() {        sync.tryRelease();    }    @Override    public Condition newCondition() {        return sync.newCondition();    }    /**     * 当前线程是否独占锁     */    public boolean isLocked() {        return sync.isHeldExclusively();    }    /**     * FIFO队列中是否有等待获取锁的 线程     */    public boolean hasQueuedThreads() {        return sync.hasQueuedThreads();    }    public static void main(String[] args) {        final MutexLock mutexLock = new MutexLock();        // ---------------------------------Task one:        new Thread(new Runnable() {            @Override            public void run() {                while (!Thread.interrupted()) {                    if (mutexLock.tryLock()) {                        try {                            System.out.println(Thread.currentThread().getName() + " acquired successfully!");                            TimeUnit.SECONDS.sleep(2);                            System.out.println(Thread.currentThread().getName() + " done!");                        } catch (InterruptedException e) {                            e.printStackTrace();                        } finally {                            mutexLock.unlock();                        }                        break;                    }                }            }        }, "Task one").start();        // --------------------------------- Task two:        new Thread(new Runnable() {            @Override            public void run() {                while (!Thread.interrupted()) {                    if (mutexLock.tryLock()) {                        try {                            System.out.println(Thread.currentThread().getName() + " acquired successfully!");                            TimeUnit.SECONDS.sleep(2);                            System.out.println(Thread.currentThread().getName() + " done!");                        } catch (InterruptedException e) {                            e.printStackTrace();                        } finally {                            mutexLock.unlock();                        }                        break;                    }                }            }        }, "Task two").start();        // --------------------------------- Task three:        new Thread(new Runnable() {            @Override            public void run() {                while (!Thread.interrupted()) {                    if (mutexLock.tryLock()) {                        try {                            System.out.println(Thread.currentThread().getName() + " acquired successfully!");                            TimeUnit.SECONDS.sleep(2);                            System.out.println(Thread.currentThread().getName() + " done!");                        } catch (InterruptedException e) {                            e.printStackTrace();                        } finally {                            mutexLock.unlock();                        }                        break;                    }                }            }        }, "Task three").start();    }}

AQS实现分析

同步队列

AQS依赖内部的同步队列(FIFO双向队列)来完成同步,当前线程获取同步状态失败时,同步器会将当前线程的引用以及等待信息构造成一个Node节点对象,并加入同步队列中,同时阻塞这个线程。当同步状态释放,会把首节点的线程唤醒,使其再次获取同步状态。

//java.util.concurrent.locks.AbstractQueuedSynchronizer.Node    .................../** * 结点是构成同步队列(等待队列也是)的基础,  * 没有成功获取同步状态的结点将被加入到队列的尾部,从队列中唤醒是从头部获取结点。 * (compareAndSetTail(...)保证加入尾部的原子性操作)  */static final class Node {    /** waitStatus value to indicate thread has cancelled */    static final int CANCELLED =  1;    /** waitStatus value to indicate successor's thread needs unparking */    static final int SIGNAL    = -1;    /** waitStatus value to indicate thread is waiting on condition */    static final int CONDITION = -2;    /**     * waitStatus value to indicate the next acquireShared should     * unconditionally propagate     */    static final int PROPAGATE = -3;     /** Marker to indicate a node is waiting in shared mode */    static final Node SHARED = new Node();    /** Marker to indicate a node is waiting in exclusive mode */    static final Node EXCLUSIVE = null;    /**     * 等待状态:     *-SIGNAL(-1):     *  后继结点处于等待状态,     *  如果当前线程释放的同步状态或者被中断,     *  将会通知后继结点,使后继结点线程运行     *      *-CANCELLED(1):     *  同步队列中等待的线程等待超时或者被中断,需要从同步队列中取消等待     *     *-CONDITION(-2):     *  结点在等待队列中,结点线程等待在这个Condition上,     *  当其他线程对这个Condition对象调用signal()\signalAll(),     *  则这个结点将进入等待队列中移入同步队列中,准备获取同步状态;     *     *-PROPAGATE(-3)     *     *-INITIAL(0):     *  初始状态     *     *     *;PROPAGATE;     */    volatile int waitStatus;    /**     * 前驱结点      */    volatile Node prev;    /**     * 后继结点     */    volatile Node next;    /**     * 获取同步状态的线程     */    volatile Thread thread;    /**     * 等待队列中的后继结点。     */    Node nextWaiter;    ...................

AQS同步队列结构
AQS同步队列结构

队尾增加等待结点
队尾增加结点

对头结点唤醒并设置为头结点
对头结点唤醒并设置为头结点

独占锁的获取与释放

这里写图片描述

public final void acquire(int arg) {        /*         * Step:         *          * 1.尝试获取锁.tryAcquire(arg)         *          * 2. addWaiter(Node.EXCLUSIVE)         * 尝试获取锁失败,则把当前线程构造为Node对象并且排他模式放入sync同步队列中.         * 同步队列的好处是:a.同步等待线程,实现公平锁(FIFO)。b.线程通信减小到最低,每个线程的等待唤醒由各自前驱结点完成。         *          * 3 acquireQueued         *          */        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))            selfInterrupt();// 线程中断标志置为true,只是顺带一个标志位的维护    }
//需要由子类实现:原子性、排他的访问state    protected boolean tryAcquire(int arg) {        throw new UnsupportedOperationException();    }
    private Node addWaiter(Node mode) {        // 此处增加了mode这个参数        Node node = new Node(Thread.currentThread(), mode);        // Try the fast path of enq; backup to full enq on failure        // 快速增加结点;如果增加操作失败,使用enq自旋方法        Node pred = tail;        if (pred != null) {// 如果null==pred,表示同步队列未初始化            node.prev = pred;            if (compareAndSetTail(pred, node)) {// 在尾部增加node结点                pred.next = node;// node结点连接之前的尾结点                return node;// 返回新的尾结点            }        }        enq(node);// 自旋, sync队列未初始化或者在队列尾部添加结点失败时执行,把node结点放入尾部.        return node;    }
 final boolean acquireQueued(final Node node, int arg) {        boolean failed = true;        try {            boolean interrupted = false;            for (;;) {// 自旋以尝试获取锁,直到发现node的前驱是头结点并且node获取状态成功,则释放头结点                final Node p = node.predecessor();// 获取当前结点(sync的尾结点)的前驱结点                if (p == head && tryAcquire(arg)) {// 如果这个前驱结点是头结点,则再次尝试获取锁.只有头结点可以获取锁                    setHead(node);// 获取锁成功,则当前结点设为头结点。头结点所对应的含义是当前占有锁且正在运行。                    p.next = null; // help GC。与上一步操作共同完成上一个头结点的释放                    failed = false;                    return interrupted;// 自旋结束,原有头结点被删除,当前结点为头结点并且获取到锁                }                if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())// parkAndCheckInterrupt:                                                                                     // 进入waiting状态,停止线程调度器对当前节点线程的调度。                    interrupted = true;// 开始进入了阻塞            }        } finally {            if (failed)                cancelAcquire(node);// 把node从sync队列删除        }    }

总结:
1.tryAcquire尝试获取状态(锁),需要保证原子性、互斥性,因为多个线程可能同时获取,使用JNI的CAS。
2.如果获取失败,addWaiter()构造Node结点并放入同步队列尾部:第一次直接把新构造的结点放入尾部(compareAndSetTail),如果失败进入enq方法,一直自旋放入队尾,直到成功为止.
3.结点放入尾部后,acquireQueued中判断当前结点的前驱结点是否为头结点、当前结点获取锁成功,领个条件满足则删除头结点,当前结点获取锁并置为头结点。如果失败,则 LockSupport.park(this)进入waiting状态,直到它的前驱结点被取消或者释放锁时才被唤醒(对应release中的unpark方法)。

独占式超时获取

两个synchronized不具备的特点:
1.可设置超时
2.响应中断。

与独占式锁非常类似,下边贴出核心代码:

   /**     * Acquires in exclusive timed mode.     *     * @param arg the acquire argument     * @param nanosTimeout max wait time     * @return {@code true} if acquired     */    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;        try {            for (;;) {                final Node p = node.predecessor();                if (p == head && tryAcquire(arg)) {                    setHead(node);                    p.next = null; // help GC                    failed = false;                    return true;                }                // 在自旋获取锁中,检查超时,一旦超时立刻返回false                nanosTimeout = deadline - System.nanoTime();                if (nanosTimeout <= 0L)                    return false;                if (shouldParkAfterFailedAcquire(p, node) && nanosTimeout > spinForTimeoutThreshold)//距离超时时间大于1000纳秒时,park效率更高;否则不去park,继续自旋                    LockSupport.parkNanos(this, nanosTimeout);                if (Thread.interrupted())//响应中断                    throw new InterruptedException();            }        } finally {            if (failed)                cancelAcquire(node);        }    }
1 0
原创粉丝点击