AQS类源码详解

一、简介

        AbstractQueuedSynchronizer类是一个极其重要的类,Lock.new Condition()返回的Condition对象就是AQS中的内部类。CyclicBarrier、CountDownLatch等类中的await方法就是调用的AQS类中的方法。

        AQS有独占(只能一个线程运行,如ReentrantLock)和共享模式(多个线程运行,如Semaphore和CountDownLatch)。

二、源码中几个关键的部分

1、state:初始化为0,表示未锁定状态,state大于0,表示已上锁。state会累加,这就是可重入的概念。但要注意,获取多少次就要释放多么次,这样才能保证state是能回到零态的。 CountDownLatch的原理便是如此。任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行完后countDown()一次,state会CAS减1。等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后余动作。

2、内部类Node

        AQS维护了一个内部类Node,用来封装每一个执行线程以及它的状态,如是否被阻塞,是否等待唤醒,是否已经被取消等。waitStatus用来区分当前节点的状态。

static final class Node {
        //
        static final Node SHARED = new Node();
        //
        static final Node EXCLUSIVE = null;

        //线程等待超时或被中断,需要从同步队列中取消该Node的结点
        static final int CANCELLED =  1;
        //线程等待执行,上一个结点唤醒后就会唤醒当前结点
        static final int SIGNAL    = -1;
        //与Condition相关,该标识的结点处于等待队列中,结点的线程等待在Condition上,当其他线程调用了Condition的signal()方法后,CONDITION状态的结点将从等待队列转移到同步队列中,等待获取同步锁
        static final int CONDITION = -2;
        //与共享模式相关,在共享模式中,该状态标识结点的线程处于可运行状态
        static final int PROPAGATE = -3;

        //当前结点的状态,共有上面4种状态 + 0,表示初始化状态
        volatile int waitStatus;

        //上一个结点
        volatile Node prev;

        //下一个结点
        volatile Node next;

        //当前线程
        volatile Thread thread;

        //condition队列中的下一个结点,与上面CONDITION这个状态有关
        Node nextWaiter;

        //
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        //获取上一个节点
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {}

        Node(Thread thread, Node mode) {     
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) {
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

3、内部类ConditionObject

        lock.newCondition()就是返回的这个对象。await方法,lock中condition.await,CyclicBarrier源码中的await都是调用的这个方法。内部有一个Node对象,当调用了conditon.await方法后,会创建当前Node节点,并添加到ConditionObject这个类中的lastWaiter,waitStatus置为-2,这样就又形成一个等待被signal唤醒的节点队列,这里称为condition队列

 public class ConditionObject implements Condition, java.io.Serializable {
        private static final long serialVersionUID = 1173984872572414699L;
        /** First node of condition queue. */
        private transient Node firstWaiter;
        /** Last node of condition queue. */
        private transient Node lastWaiter;

三、共享模式流程

1、共享模式主要实现类是ReentrantReadWriteLock中的ReadLock。ReentrantReadWriteLock lock = new ReentrantReadWriteLock();   lock.readLock().lock();

2、首先调用tryAcquireShared尝试获取共享锁

 public final void acquireShared(int arg) {
        if (tryAcquireShared(arg) < 0)
            doAcquireShared(arg);
 }
protected final int tryAcquireShared(int unused) {
            Thread current = Thread.currentThread();
            int c = getState();
            //如果state不等于0且当前线程不是被阻塞的线程,就返回-1,表示不能获取共享锁
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            //将state的值无符号右移16位
            int r = sharedCount(c);
            //判断头节点的下一个节点是否是共享模式,如果是共享模式,将state值加65535
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
                    //如果r等于0,当前线程就是头节点,count等于1
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
                    firstReaderHoldCount++;
                } else {
                    HoldCounter rh = cachedHoldCounter;
                    if (rh == null || rh.tid != getThreadId(current))
                        cachedHoldCounter = rh = readHolds.get();
                    else if (rh.count == 0)
                        readHolds.set(rh);
                    rh.count++;
                }
                return 1;
            }
            return fullTryAcquireShared(current);
        }

四、独占模式流程

1、创建对象Lock lock = new ReentrantLock();   Condition condition = lock.newCondition();

2、线程1调用lock.lock方法,然后默认调用NonfairSync类的lock方法。(ReentrantLock默认非公平锁)。lock是可重入锁,第一次调用lock,会将Node中的state值从0变成1。如果又继续调用lock,会进入acquire(1)方法,将state再加1,变成2。以此类推。不过释放的时候不用释放多次,详情可见fullRelease方法。

3、如果还没被上锁,那么state变为1。

4、如果已经上锁了,会进入acquire方法,将当前节点添加到Node链表的tail处,随后Lock.park(this)。当被唤醒后,就轮到当前节点上锁了,如果tryAcquire成功,就把当前节点设置为头节点。至此,完成上锁的全程。

 final void lock() {
        //将state从0变为1,state是1表示没有加锁,否则表示加锁了
        if (compareAndSetState(0, 1))
            //将当前线程赋值给成员变量,这样别的线程要执行时可以用这个成员变量判断是否是上锁的线程,用来实现独占锁、重入锁
            setExclusiveOwnerThread(Thread.currentThread());
        else
            //如果把state从0变成1失败,说明当前线程已经加过锁了。此时判断该线程是否等于exclusiveOwnerThread,如果是的话就把state再加1
            acquire(1);
 }
public final void acquire(int arg) {
    
    //tryAcquire:尝试上锁。获取当前Node的state值,如果等于0,表示未上锁,就加锁。如果不是0,且也是阻塞的线程,就将state+1,若不是阻塞的线程,返回false。
    //addWaiter:生成当前的Node节点对象,并添加到Node链表中的tail节点。
    //获取当前节点的上一个节点,如果节点的waitStatus等于-1,表示需要唤醒,就阻塞住。
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}
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;
                }
                //如果当前节点不具备上锁条件,那么就要判断是否应该阻塞(waitStatus是否等于-1)
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

5、调用condition.await(),先释放锁(严格讲是unpark头节点的下一个节点),再阻塞住当前节点。详细见下方代码。

public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        //将当前线程添加到ConditionObject类的Node节点队列中结尾处
        Node node = addConditionWaiter();
        //将state值置为0,并将Node中的头节点放行
        int savedState = fullyRelease(node);
        int interruptMode = 0;
        //判断Node的waitStatus是否等于-2,如果是就阻塞。
        while (!isOnSyncQueue(node)) {
            LockSupport.park(this);
            if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                break;
        }
        if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
            interruptMode = REINTERRUPT;
        if (node.nextWaiter != null) // clean up if cancelled
            unlinkCancelledWaiters();
        if (interruptMode != 0)
            reportInterruptAfterWait(interruptMode);
}
final int fullyRelease(Node node) {
    boolean failed = true;
    try {
        //得到节点的state值,正常情况下是1
        int savedState = getState();
        //
        if (release(savedState)) {
            failed = false;
            return savedState;
        } else {
            throw new IllegalMonitorStateException();
        }
    } finally {
        if (failed)
            node.waitStatus = Node.CANCELLED;
    }
}
public final boolean release(int arg) {
    //是否可以释放锁,如果可以,把exclusiveOwnerThread置为null,并把node中的state值变为0
    if (tryRelease(arg)) {
        //找到等待队列中的头节点,如果头节点不为null且不是空闲状态,就释放该节点锁。这个头节点是在第二个线程调用lock方法时才会赋值。
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        return true;
    }
    return false;
}
//将当前state的值减去传过来的参数,如果是0,则表示可以释放锁
protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    //如果当前await的线程不是lock的线程,就报错(await必须放在lock里)
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    //释放锁,将成员变量中的线程变量置为null,然后把当前线程的state变成0,表示释放锁
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */

        //如果下一个节点为null或处于被中断状态,就移除这个节点,把下一个处于阻塞状态的节点移过来
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //节点解锁
        if (s != null)
            LockSupport.unpark(s.thread);
    }

6、condition.signal()。找到condtion队列中的头节点firstWaiter,并放到Node队列的tail中。signal释放锁是根据Node队列中节点的顺序释放的,是公平的。 

private void doSignal(Node first) {
            do {
                //把头节点置为下一个节点
                if ( (firstWaiter = first.nextWaiter) == null)
                    lastWaiter = null;
                first.nextWaiter = null;
            } while (!transferForSignal(first) &&
                     (first = firstWaiter) != null);
        }
final boolean transferForSignal(Node node) {
        //把节点的waitStatus置为0
        if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
            return false;

        //把这个节点添加到Node节点队列的tail中去
        Node p = enq(node);
        int ws = p.waitStatus;
        if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
            LockSupport.unpark(node.thread);
        return true;
    }
private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) {
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                //这个CAS操作后,tail=node,但t还是等于原来的tail。因为改变的是tail指针存储的内存地址。
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

 

 7、lock.unlock()。把Node中的head节点放行。

五、总结

AQS中有个state值,为0表示未上锁,大于0表示上锁了。ReentrantLock调用lock上锁时,state会加1,如果又调用lock,由于重入锁,state再加1。如果调用unlock,就将state减1,当减到0时,就将等待队列中的头节点释放锁。如果调用了condtion.await,就将该线程放入等待队列中,把state值置为0,ExclusionThread置为null,然后阻塞住线程,并释放等待队列中头节点的线程。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值