AQS详解

AQS(AbstractQueuedSynchronizer)是Java里面的一个同步管理器,Java里面所有的锁的实现的核心都是AQS,例如ReentrantLock,CountDownLatch。对资源,和资源分配方式的管理。

AQS中维护了一个双向链表(FIFO队列)

 AQS里面会存在 head,tail,state。分别表示队列的头节点,尾节点和锁次数。

 /**
     * Head of the wait queue, lazily initialized.  Except for
     * initialization, it is modified only via method setHead.  Note:
     * If head exists, its waitStatus is guaranteed not to be
     * CANCELLED.
     */
    private transient volatile Node head;

    /**
     * Tail of the wait queue, lazily initialized.  Modified only via
     * method enq to add new wait node.
     */
    private transient volatile Node tail;

    /**
     * The synchronization state.
     */
    private volatile int state;

 

首先来看一下队列中的元素吧,队列中的每一个元素都用一个Node表示

 static final class Node {
        /** 共享模式下等待的标记 */
        static final Node SHARED = new Node();
        /** 独占模式下等待的标记 */
        static final Node EXCLUSIVE = null;

        /** 线程等待状态 表示线程已经被取消 */
        static final int CANCELLED =  1;
        /** 线程等待状态,表示线程被挂起需要被唤醒 */
        static final int SIGNAL    = -1;
        /** 线程等待状态 表示线程在Condition上 */
        static final int CONDITION = -2;
        /**
         *表示下一个acquireShared需要无条件传播
         */
        static final int PROPAGATE = -3;

        /**
         *  waitStatus的初始值时0,使用CAS来修改节点的状态
         */
        volatile int waitStatus;

        /**
         * 当前节点的前驱节点
         */
        volatile Node prev;

        /**
         * 当前节点的后继节点
         */
        volatile Node next;

        /**
         * 当前节点的线程
         */
        volatile Thread thread;

        /**
         * 链接到下一个节点的等待条件
         */
        Node nextWaiter;

}

接下来我们来通过具体的ReentrantLock锁的流程来讲解时如何基于AQS来实现的可重入锁。

public class ReentrantLock implements Lock, java.io.Serializable {
   
    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();
            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;
        }

        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() {
            // While we must in general read state before owner,
            // we don't need to do so to check if current thread is owner
            return getExclusiveOwnerThread() == Thread.currentThread();
        }

        final ConditionObject newCondition() {
            return new ConditionObject();
        }

        // Methods relayed from outer class

        final Thread getOwner() {
            return getState() == 0 ? null : getExclusiveOwnerThread();
        }

        final int getHoldCount() {
            return isHeldExclusively() ? getState() : 0;
        }

        final boolean isLocked() {
            return getState() != 0;
        }

        /**
         * Reconstitutes the instance from a stream (that is, deserializes it).
         */
        private void readObject(java.io.ObjectInputStream s)
            throws java.io.IOException, ClassNotFoundException {
            s.defaultReadObject();
            setState(0); // reset to unlocked state
        }
    }

    /*
     *  非公平锁
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

    /**
     * 公平锁
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

        final void lock() {
            acquire(1);
        }

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

首先在ReentrantLock类内部,定义了Sync静态内部类以及它的子类NonfairSync和FairSync。

接着我们来看一下我们自己的代码

Lock lock = new ReentrantLock();
        lock.lock();
        lock.unlock();

首先创建一个ReentrantLock 

public class ReentrantLock implements Lock, java.io.Serializable {
    ......
 public ReentrantLock() {
        sync = new NonfairSync();
    }

    /**
     * Creates an instance of {@code ReentrantLock} with the
     * given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
}

默认无参构造方法会去创建一个公平锁NonfairSync。使用有参构造方法传入fair值,如果伟true则创建公平锁,如果为fasle则是非公平锁。

接下来是 lock.lock(); 调用加锁方法,由于我们使用的无参构造创建的NonfairSync实例,所以会去调用它的lock方法。

//ReentrantLock.java
final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }


//AbstractQueuedSynchronizer.java
protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

//AbstractOwnableSynchronizer.java
 protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

第一步compareAndSetState(0, 1)判断当前对象是否被获取了锁。根据传入的期望值和修改值以及当前状态偏移量判断是否可以获取锁。如果可以过去锁,则将exclusiveOwnerThread赋值为当前线程。如果获取不到锁,则调用 acquire(1);方法

//AbstractQueuedSynchronizer.java
public final void acquire(int arg) {
            //Node.EXCLUSIVE 传入独占锁机制
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }



//ReentrantLock.java
 protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }

final boolean nonfairTryAcquire(int acquires) {
            //获取当前线程
            final Thread current = Thread.currentThread();
            //获取当前锁次数
            int c = getState();
            //如果锁次数为0
            if (c == 0) {
                //判断是否可以上锁
                if (compareAndSetState(0, acquires)) {
                    //将exclusiveOwnerThread赋值为当前线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            //如果当前线程等于exclusiveOwnerThread
            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;
        }

首先!tryAcquire(arg)判断,addWaiter(Node.EXCLUSIVE), arg)

private Node addWaiter(Node mode) {
        //把当前线程封装成一个node
        Node node = new Node(Thread.currentThread(), mode);
        // 记录tail节点
        Node pred = tail;
        //如果tail节点不等于null
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }

在addwaiter中 首先会把当前线程封装成一个node 记录tail的值。由于首次创建tail肯定为null,所以执行 enq(node)方法

private Node enq(final Node node) {
        for (;;) {
            Node t = tail;
            if (t == null) { // (1)操作
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {//(2)操作
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

由于尾节点tail为null,所以执行(1)会创建一个头节点,把head赋值给tail,创建一个为空的双向链表,

接着执行(2),  node.prev = t; 将新的threadB节点的pre节点指向tail也就是空节点,compareAndSetTail(t, node) 将tail节点指向新的threadB节点,t.next = node;将空节点的next节点指向threadB节点。

接下来看一下acquireQueued方法,对除了head节点以外其他的节点进行挂起,减少cpu开销。

final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false; 
            //自旋
            for (;;) {
                //获取当前封装节点的prev节点
                final Node p = node.predecessor();
                //判断p == head  tryAcquire尝试获取锁
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }    
                //shouldParkAfterFailedAcquire 如果通过增强锁失败 是不是要通过Park挂起
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }



private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        获取当前节点的前置节点的waitStatus状态
        int ws = pred.waitStatus;
        //如果等于SIGNAL(-1)则返回true
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /* 将前置节点置为SIGNAL状态
             * /
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }


 private static final boolean compareAndSetWaitStatus(Node node,
                                                         int expect,
                                                         int update) {
        return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                        expect, update);
    }

假设线程B的流程

1 进去acquireQueued 方法,首先自旋

2 发现前驱节点是头节点,但是尝试获取锁失败,

3 会执行shouldParkAfterFailedAcquire 方法,由于head节点的状态为0 不是-1,

4  所以会执行compareAndSetWaitStatus方法,将前置节点置为-1

然后会重新从1开始执行,但是到第3步的时候发现head节点是-1,返回true,接着执行parkAndCheckInterrupt()方法。

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

通过LockSupport.park将当前的线程节点挂起。阻塞住。

 public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        setBlocker(t, blocker);
        UNSAFE.park(false, 0L);
        setBlocker(t, null);
    }

然后返回currentThread().isInterrupted(true);

public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

最后执行acquire方法里面的selfInterrupt();通知线程应该中断。

 static void selfInterrupt() {
        Thread.currentThread().interrupt();
    }

上面便是加锁的全部过程。

接下来看看解锁的过程

 

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

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

首先尝试release

//ReentrantLock.java
protected final boolean tryRelease(int releases) {
            记录当前锁次数 - 1
            int c = getState() - releases;
            //判断当前线程是否等于exclusiveOwnerThread
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            boolean free = false;
            //如果锁记录数为0
            if (c == 0) {
                free = true;
                //释放锁
                setExclusiveOwnerThread(null);
            }
            setState(c);
            return free;
        }

调用unparkSuccessor

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.
         */
        //获取后置节点
        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;
        }
        //如果后置节点不为Null
        if (s != null)
            //唤醒后置节点
            LockSupport.unpark(s.thread);
    }

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值