AQS源码

博客地址:https://monkeysayhi.github.io/2017/12/05/%E6%BA%90%E7%A0%81%7C%E5%B9%B6%E5%8F%91%E4%B8%80%E6%9E%9D%E8%8A%B1%E4%B9%8BReentrantLock%E4%B8%8EAQS%EF%BC%881%EF%BC%89%EF%BC%9Alock%E3%80%81unlock/


ReentrantLock和ReentrantReadWriteLock从类的形式上看并没有关联,但是底层都是采用AQS的实现
我们知道我们JDK 的锁都是基于AQS
而在其上面主要有读写锁(即所谓的独占锁和非独占锁)
方法也只有lock 或者unlock
除了锁 还有condition
这些锁都支持可重入
公平锁和非公平锁的最大区别就是:非公平锁是先尝试获取锁,获取不到在按照公平锁的方法进入队列排队获取
锁为什么不能升级--升级会死锁,目的也是为了数据可见性,如果读锁已经被多个线程获取,其中任意线程成功获取了写锁并且更新了数据,这个更新对其他已经获取到读锁的线程是不可见的。

  1. 当有线程获取读锁时,不允许再有线程获得写锁

  2. 当有线程获得写锁时,不允许其他线程获得读锁和写锁

为什么要支持锁降级
降级的过程为持有写锁,把持住不释放并获取读锁,然后再释放写锁,如果释放写锁再获取读锁则不能称之为锁降级。支持降级的原因是为了实现读写连续,减少线程切换的开销。如果不能降级的话,A线程在对数据修改完毕后立马释放写锁,假如A线程后续紧跟一个读的操作,在获取读锁之前B线程拿到了写锁,由于读写不能并发,A线程就只能阻塞

首先我们分析下ReentrantLock

获取锁,如果获取不到则会一直阻塞
不会响应中断
readLock.lock();
尝试获取锁,获取不到就返回 不会响应中断
readLock.tryLock();
获取锁,如果获取不到则会一直阻塞
会响应中断
readLock.lockInterruptibly();
尝试获取锁,获取不到就等待一段时间 会响应中断
readLock.tryLock(10,TimeUnit.SECONDS);

ReentrantLock的lock源码
 public final void acquire(int arg) {
 首先尝试获取锁失败,则加入等待队列,acquireQueued返回true代表
 在等待获取锁的过程中中断过
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            因为之前中断后,AQS恢复了中断标识,这边自我中断下吧中断标识
            设置为true。这样如果有业务逻辑需要判断中断标识,可以得知是否中断过
            selfInterrupt();
    }
    非公平锁是先尝试获取锁
      final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            因为是独占锁
            int c = getState();
            当锁没有被持有的时候,直接去获取 不需要管是否有其他人在等待
            if (c == 0) {
            设置state
                if (compareAndSetState(0, acquires)) {
                设置独占标识的线程
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }如果当前线程等于独占标识的线程
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                更新state,如果state小于0 代表重入超过最大值了
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            获取失败
            return false;
        }
        公平的获取锁
          protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            如果这个锁还没人获取
            if (c == 0) {
            如果不存在等待队列且设置state成功 即获取了锁,设置独占标识的线程
                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;
        }

        
            public final boolean hasQueuedPredecessors() {
      
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
        h!=t 代表最少有2个元素 一个是head 一个是tail
        return h != t &&
        第一个如果为true 正好执行到tail了 即s=tail节点
        否则只要判断下s.thread不等于当前线程
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }
    
    按照锁的模式把当前线程包装成节点
    注意初始化之后head和老的tail节点的next都是第一个正常节点,也就是新的tail节点
    而不是新的head-->old tail-->新的tail 真正的是head-->新的tail 和old tail-->新的tail
    其中head和oldtail 是同一个对象的两种不同的引用 也就是说最终就是head-->tail tail是正常节点
    head是dummpy节点
     private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
    首先利用cas直接放入尾部
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
for循环利用cas放入队列
        enq(node);
        return node;
    }
    
    这边将tail赋值给t非常好,当我们将node设置尾部tail成功时候
t还是只想tail前一个继续操作next绑定node,就算这个时候并发另外一个线程很快
也设置tail成功也不影响前面的设置
当head=tail说明刚初始化好

   private Node enq(final Node node) {
        for (;;) {
获取尾部 
            Node t = tail;
如果尾部不存在,说明还没初始化,先初始化队列
            if (t == null) { // Must initialize
设置head节点 如果成功了就把head和tail 赋值为一个new出来的node节点,然后在重新循环将该队列设置到尾部,刚初始化的节点是没用的后期都会移除
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
尝试把节点加入尾部,这边存在 pre一致 但是原始的tail的next 还是null的情况
                node.prev = t;
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }
循环等待该node获取锁,并记录自己被中断
 final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
获取node的前置节点,前置节点有可能是正常的node
也有可能是head和tail 这种刚初始化dummpy节点
                final Node p = node.predecessor();
如果前置节点就是head,那么就尝试获取下锁(这时候程序在运行的情况分为
刚加入队列,或者被head节点唤醒且正在释放锁或者还未释放完)
                if (p == head && tryAcquire(arg)) {
获取锁成功把这个node设置为head
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
返回
                    return interrupted;
                }
如果p不等head 或者获取锁失败,
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
执行到这一步且failed=true 代表发送了异常,取消获取
            if (failed)
                cancelAcquire(node);
        }
    }

waitStatus四种状态
SIGNAL:标识这个节点如果后期获取到锁 在释放的时候需要唤醒他的后继节点
CANCELLED:因为超时或者被中断,其线程不会再阻塞
CONDITION:这个还记得咱们的Condition这个对象吗?这个代表节点还在condition队列中 还未进入咱们的同步队列
PROPAGATE:调用共享锁的时候的是否应该无条件的传播
head节点就是signal其后面的正常节点就是condition
0:刚创建的节点都是0,这个方法就是把所以的0都转换为signal 
 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
 假如是刚初始化的时候 那么head的waitstatus是0
        int ws = pred.waitStatus;
        当SIGNAL时候代表head节点正在持有锁 还没释放锁 所以后置节点应该沉睡
        等待head把他唤起
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
            如果大于0说明节点是被取消的 应该移除这些节点
            
        if (ws > 0) {
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev;
                这边有个问题 pre这个废弃节点如果想完全移除出链表
                不需要  pred.next = null  因为cancelAcquire(node); 已经
                执行过这段代码了
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
             一般到这一步了说明是0或者是PROPAGATE 尝试把head节点设置为
             SIGNAL
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
阻塞,然后返回释放interupt,是的话设置interupt标识
    private final boolean parkAndCheckInterrupt() {
    该线程沉睡
        LockSupport.park(this);
        return Thread.interrupted();
    }
取消获取失败的节点
 private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;
        将该节点的thread置空
        node.thread = null;
如果node的前置节点是取消状态,直接跳过
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        前置节点的next正常没有并发的情况下就是我们的node节点
        Node predNext = pred.next;
        将节点状态变为取消
        node.waitStatus = Node.CANCELLED;

     如果当前节点就是tail 那么我们把tail设置为pre(pre有可能是head 也有可能是其他的节点)
        if (node == tail && compareAndSetTail(node, pred)) {
            把这个节点的next设置为null
            compareAndSetNext(pred, predNext, null);
        } else {
            说明不是tail 
            我们看看如果pre不是head 且pre是SIGNAL或者 ws<0 我们把pre的waitstatus设置为signal
            且pre的thread不为空
            int ws;
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                获取node的下一个正常节点塞入pre的next
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
            如果是head 那么我们就需要唤醒下一个节点
                unparkSuccessor(node);
            }
            把脱离的节点从链表中拿出来
            node.next = node; // help GC
        }
    }

    
       private void unparkSuccessor(Node node) {
            设置waitstatus为0,因为他的下一个节点不需要signal,我们正在signal
            设置为0失败也没关系
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
        如果节点的下一个是null 或者为取消状态
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            从尾部开始寻找,node节点不等于当前且waitstatus小于等于0
            假如你从前往后遍历的动作发生在enq方法中的cas之后和赋值之前最后一个节点是漏掉的
            在enq中我们是先把新的tail的pre和老的tail 链接在一起 等cas成功了 在设置老tail的next和新的tail
            如果我们从前往后遍历的时间刚好发生在cas之和赋值之前 那么我们tail节将被遗漏
            但是我们这边唤醒最后一个可用的 也不用怕,因为其前置节点不是head 所以其不会获得锁
            但是会帮我们清理取消的节点 这样就可以正常的获取下一个节点进行唤醒获得锁的操作
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        唤醒该线程
        if (s != null)
            LockSupport.unpark(s.thread);
    }

lockInterruptibly

  public final void acquireInterruptibly(int arg)
            throws InterruptedException {
如果检测到线程中的 抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
获取线程
        if (!tryAcquire(arg))
这个过程我们之前分析过至少这边如果检测到中断就直接抛出
我们正常的节点唤醒是通过unsafe的unpark方法
            doAcquireInterruptibly(arg);
    }


ReentrantLock的trylock 底层默认的是采用unfaire,我们上面已经分析过了,获取不到直接返回
ReentrantLock的trylock 携带时间的参数底层也有多种实现 ,可以响应中断
    public final boolean tryAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        return tryAcquire(arg) ||
            doAcquireNanos(arg, nanosTimeout);
    }
我们主要分析下如何做到时间控制
   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;
                }
                nanosTimeout = deadline - System.nanoTime();
                if (nanosTimeout <= 0L)
                    return false;
如果未获取到通过LockSupport.parkNanos来沉睡
                if (shouldParkAfterFailedAcquire(p, node) &&
                    nanosTimeout > spinForTimeoutThreshold)
                    LockSupport.parkNanos(this, nanosTimeout);
                if (Thread.interrupted())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
ReentrantLock的unlock
   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;
    }

   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);
            }
重新设置state
            setState(c);
            return free;
        }

再分析ReentrantReadWriteLock.WriteLock

ReentrantReadWriteLock.WriteLock 的lock
  public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
  主要是tryAcquire与reentrant不一样我们分析下
     protected final boolean tryAcquire(int acquires) {
       
            Thread current = Thread.currentThread();
            int c = getState();
            int w = exclusiveCount(c);
当c!=0 w=0 代表共享锁被获取了 但是独占锁没有,而我们此时是获取独占锁的
所以直接返回,也就是说代码中直接拒绝 了我们 不允许 读锁降级为写锁
当w!=0 且不是独占锁的线程也不可获取锁
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
如果获取锁超过了最大值 抛出异常
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
设置state状态
                setState(c + acquires);
                return true;
            }
对于公平锁来说 当锁还未被获取,且有队列存在 那么write应该block
对于非公平来说直接返回false
            if (writerShouldBlock() ||
尝试获取锁
                !compareAndSetState(c, c + acquires))
                return false;
获取成功设置独占标识
            setExclusiveOwnerThread(current);
            return true;
        }
ReentrantReadWriteLock.ReadLock的lock

共享锁---当我们的写锁被其他线程持有 那么我们是不可以获取读锁,写锁排斥任何的锁

   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();
如果写锁存在而且非本线程持有 直接返回
            if (exclusiveCount(c) != 0 &&
                getExclusiveOwnerThread() != current)
                return -1;
            int r = sharedCount(c);
公平方面主要就是看看是否存在等待队列
 非公平方式的实现主要就是判断head的下一个是否是写锁,也就是说如果是血锁,就不去抢,否则就去抢锁
            if (!readerShouldBlock() &&
                r < MAX_COUNT &&
SHARED_UNIT可以留意
                compareAndSetState(c, c + SHARED_UNIT)) {
                if (r == 0) {
如果是第一次获取 设置 firstReader 和firstReaderHoldCount 
                    firstReader = current;
                    firstReaderHoldCount = 1;
                } else if (firstReader == current) {
如果是第一个线程重复获取 更新 firstReaderHoldCount
                    firstReaderHoldCount++;
                } else {
代表是其他线程获取
cachedHoldCounter 代表最后一个线程的对象,依赖threadlocal存储着重入的次数
                    HoldCounter rh = cachedHoldCounter;
如果是第一次被其他线程获取或者多次被其他线程获取 (通过对比id)
                    if (rh == null || rh.tid != getThreadId(current))
生成一个新的cachedHoldCounter 
                        cachedHoldCounter = rh = readHolds.get();
是当前线程且rh不等于null
                    else if (rh.count == 0)
                        readHolds.set(rh);
给count增加1
                    rh.count++;
                }
                return 1;
            }
轮询的方式重复了上面的步骤
            return fullTryAcquireShared(current);
        }
  执行到这一步要么前面是head的next存在写锁(非公平)
要么是就是公平锁下面有队列   
     final int fullTryAcquireShared(Thread current) {
     
            HoldCounter rh = null;
            for (;;) {
                int c = getState();
先检测写锁是否被其他线程获取,如果是直接返回
                if (exclusiveCount(c) != 0) {
                    if (getExclusiveOwnerThread() != current)
                        return -1;

                } else if (readerShouldBlock()) {
如果write应该block 如果firstReader == current
可以尝试去获取读锁
          
                    if (firstReader == current) {
                        // assert firstReaderHoldCount > 0;
                    } else {
如果rh==null,检测最后一个获取读锁的线程是否有问题
                        if (rh == null) {
                            rh = cachedHoldCounter;
                            if (rh == null || rh.tid != getThreadId(current)) {
                                rh = readHolds.get();
                                if (rh.count == 0)
                                    readHolds.remove();
                            }
                        }
这边如果获取锁不可能count=-1 直接返回
                        if (rh.count == 0)
                            return -1;
                    }
                }
超出获取的最大值 抛出异常
                if (sharedCount(c) == MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
重复了我们上面的步骤
                if (compareAndSetState(c, c + SHARED_UNIT)) {
                    if (sharedCount(c) == 0) {
                        firstReader = current;
                        firstReaderHoldCount = 1;
                    } else if (firstReader == current) {
                        firstReaderHoldCount++;
                    } else {

                        if (rh == null)
                            rh = cachedHoldCounter;
                        if (rh == null || rh.tid != getThreadId(current))
                            rh = readHolds.get();
                        else if (rh.count == 0)
                            readHolds.set(rh);
                        rh.count++;
                        cachedHoldCounter = rh; // cache for release
                    }
                    return 1;
                }
            }
        }

 private void doAcquireShared(int arg) {
加入节点
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
如果前置节点是head 就获取锁,
                if (p == head) {
                    int r = tryAcquireShared(arg);
获取成功就设置head
                    if (r >= 0) {
设置head并释放写锁
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }

readerShouldBlock
公平方面主要就是看看是否存在等待队列
 非公平方式的实现主要就是判断head的下一个是否是写锁,也就是说如果是血锁,就不去抢,否则就去抢锁
    final boolean apparentlyFirstQueuedIsExclusive() {
        Node h, s;
        return (h = head) != null &&
            (s = h.next)  != null &&
            !s.isShared()         &&
            s.thread != null;
    }

共享锁的是否就是,先去消除线程中持有锁的计数,然后在修改state

protected final boolean tryReleaseShared(int unused) {
            Thread current = Thread.currentThread();
            if (firstReader == current) {
                // assert firstReaderHoldCount > 0;
                if (firstReaderHoldCount == 1)
                    firstReader = null;
                else
                    firstReaderHoldCount--;
            } else {
                HoldCounter rh = cachedHoldCounter;
                if (rh == null || rh.tid != getThreadId(current))
                    rh = readHolds.get();
                int count = rh.count;
                if (count <= 1) {
                    readHolds.remove();
                    if (count <= 0)
                        throw unmatchedUnlockException();
                }
                --rh.count;
            }
            for (;;) {
                int c = getState();
                int nextc = c - SHARED_UNIT;
                if (compareAndSetState(c, nextc))
                    // Releasing the read lock has no effect on readers,
                    // but it may allow waiting writers to proceed if
                    // both read and write locks are now free.
                    return nextc == 0;
            }
        }

你可能感兴趣的:(AQS源码)