【并发】AQS源码分析

AQS全称是AbstractQueuedSynchronizer,是JDK提供的一个同步器设计框架,很多并发数据结构如ReentrantLock、ReentrantReadWriteLock、Semaphore等都是基于AQS来实现的,下面来分析一下AQS的原理。

 

一、底层数据结构

AQS底层维护了一个state(代表共享资源)和一个CLH队列(代表线程等待队列)

state:state是一个volatile int型变量,用以表示共享资源的使用情况。

head、tail:分别表示CLH队列的头节点和尾节点。关于CLH队列原理可以看这篇博客:

private transient volatile Node head;

private transient volatile Node tail;

private volatile int state;

AQS定义了两种资源:

一种是Exclusive(独占方式,同一时间只有一个线程能够访问,比如ReentrantLock);

一种是Share(共享方式,同一时间允许多个线程访问,比如Semaphore)。

 

二、AQS原理

AQS被定义为一个同步器框架,那么为什么说AQS是一个“框架”呢,框架的定义应该是使用者根据框架的要求去实现一些业务逻辑,然后底层由框架自己去完成,比如使用Hadoop大数据框架的时候,我们只需要定义好Map/Reduce函数就可以了,其他复杂的底层操作就有Hadoop帮我们完成。AQS也应该是这样一个东西,事实上,AQS也确实是这样做的,它只需要开发者实现其中的几个函数,其他的底层操作比如CLH队列操作、CAS自旋等等都是由AQS框架来实现。

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

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

protected int tryAcquireShared(int arg) {
    throw new UnsupportedOperationException();
}

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

protected boolean isHeldExclusively() {
    throw new UnsupportedOperationException();
}

上面的几个方法就是AQS要我们实现的函数,这几个方法都是AQS提供给开发者编写代码逻辑的,因此每个方法里面都定义为直接抛出UnsupportedOperationException,下面解释一下这几个函数的作用。

tryAcquire:尝试获取独占资源。成功返回true,失败返回false。

tryRelease:尝试释放独占资源。成功返回true,失败返回false。

tryAcquireShared:尝试获取共享资源。成功返回一个非负数,代表剩余资源(0就表示没有可用资源),负数表示失败。

tryReleaseShared:尝试释放共享资源。成功返回true,失败返回false。

isHeldExclusively:判断线程是否正在独占资源。

对于独占方式的同步器,只需要实现tryAcquire-tryRelease;而对于共享方式的同步器则只需要实现tryAcquireShared-tryReleaseShared就行了,当然如果自定义同步器需要同时实现独占和共享方式的话也可以,比如读写锁ReentrantReadWriteLock,其中读锁是共享的,写锁是独占的。

 

三、源码解析

AQS中对于资源获取的顶层接口是acquire-release和acquireShare-releaseShare,下面从这些顶层接口来分析:

1、acquire

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

代码逻辑:

(1)尝试获取资源(tryAcquire),成功则返回,失败则跳转到(2)

(2)请求将该线程加入线程等待队列的尾部,并标记为独占模式(addWaiter(Node.EXCLUSIVE)),并不断请求队列智斗获取到资源,如果该过程被打断,则跳转到(3),失败则返回。

(3)自我中断(selfInterrupt)

(4)完成

 

2、release

    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

代码逻辑:

(1)尝试释放资源,如果成功则跳转到(2),失败则返回false

(2)获取线程等待队列的头节点,如果头节点线程不为空且处于等待状态,则唤醒该线程(unparkSuccessor)

 

3、acquireShared

    public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
    }

代码逻辑:

(1)尝试获取共享资源,如果获取失败则调用doAcquireShared自旋来请求共享资源。

 

4、releaseShared

    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

代码逻辑:

(1)尝试释放共享资源,释放成功则唤醒等待队列中的后继节点来获取资源。

 

5、如果需要响应中断程序的话,AQS还提供了acquireInterruptibly和acquireSharedInterruptibly函数来响应。

 

4、实际例子:ReentrantLock解析

一般用AQS来实现同步器主要使用内部类的方式来实现的,ReentrantLock里面就是一个比较经典的实现,看一下这个内部类的实现源码:

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;

        @ReservedStackAccess
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }

        @ReservedStackAccess
        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);
            }
            setState(c);
            return free;
        }

        protected final boolean isHeldExclusively() {
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        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;
        }

        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

这里定义的Sync同步器类仍然是一个抽象类,是因为ReentrantLock同时支持公平锁和非公平锁,而这两种同步方式是不一样的,所以先定义一个Sync类作为两种实现的基类,后面会看到具体的公平锁和非公平锁的实现。

由于ReentrantLock本身是一个独享锁,因此Sync类主要重写了AQS里面的tryRelease和isHeldExclusively方法,同时定义了非公平锁的nonfairTryAcquire方法作为请求资源的方法。

 

非公平锁实现:

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

其实基类Sync完完全全就是一个非公平锁的实现,所以NonfairSync并没有新代码,tryAcquire直接调用nonfairTryAcquire。

 

公平锁实现:

    static final class FairSync extends Sync {
        @ReservedStackAccess
        protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }

公平锁与非公平锁的区别也就是资源请求的方式不一样,所以在基类Sync的基础上重写tryAcquire方法,顺序唤醒等待队列中的线程。

 

基于AQS实现了公平锁和非公平锁之后,如何实现上锁和解锁的功能也就十分简单了,源码直接用一行代码就实现了:

    public void lock() { 
        sync.acquire(1); 
    }

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

 

你可能感兴趣的:(java,并发)