AQS源码解析3.release()方法

以ReentrantLock中的公平锁的unlock为例。

1.大致流程图

在这里插入图片描述

2.AQS.release

unlock底层调用的是release()

    public final boolean release(int arg) {
    
    
        
        /*
         *  tryRelease()尝试释放锁 
         *  true -> 表示当前线程已经完全释放锁 
         *  false -> 表示当前线程尚未完全释放锁
         */
        if (tryRelease(arg)) {
    
    
            //队列头结点
            Node h = head;
            
            /*
             *   h != null 说明队列不为空
             *   h.waitStatus != 0 (大概率是-1(Signal)) 说明当前head后一定插入过				 *   node节点。可以进行唤醒节点的操作。
             */
            if (h != null && h.waitStatus != 0)
                /*
                 * 唤醒后继节点(注意,unparkSuccessor里也有寻找后继的逻辑,即唤醒的不一				  *	定就是h.next节点)
                 */
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

3.ReentrantLock.tryRelease

        protected final boolean tryRelease(int releases) {
    
    
            
            //c = 当前锁的状态 - 释放的锁的状态
            int c = getState() - releases;
            
            //这里判断 当前调用释放锁的线程是否是获取锁的线程,不是的话直接抛出异常。
            if (Thread.currentThread() != getExclusiveOwnerThread())
                throw new IllegalMonitorStateException();
            
            //free -> 是否已经完全释放锁。
            boolean free = false;
            
            //c == 0,表示满足完全释放锁的条件
            if (c == 0) {
    
    
                //free 置为true
                free = true;
                //设置当前独占锁的线程为NULL。
                setExclusiveOwnerThread(null);
            }
            //更新state
            setState(c);
            //返回free的值。(完全释放锁返回true 反之返回false)
            return free;
        }

4.AQS.unparkSuccessor

    //唤醒当前节点的后继节点
	private void unparkSuccessor(Node node) {
    
    
        
        //获取当前节点的waitStatus
        int ws = node.waitStatus;
        
        //小于0 就是-1(Signal) 使用CAS的方式将状态变为0
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);     
		
        
 		//获取当前当前节点的后继节点 
        Node s = node.next;
        
        /*
         *  这里就是判断当前节点的后继节点的状态是否是0(初始状态)或者-1(Signal),
         *  不是的话,就去队列中找到一个距离当前节点最近的可以被唤醒的节点赋值给s。
         */
        if (s == null || s.waitStatus > 0) {
    
    
            s = null;
            
            //在队列中找到一个距离当前节点最近的并且可以被唤醒的节点,
        for (Node t = tail; t != null && t != node; t = t.prev)
            	/*
            	 * 状态合法,赋值给s,注意这里找到时并没有break,即最终找到的节点就是离				 * node最近的节点
            	 */
                if (t.waitStatus <= 0)
                    s = t;
        }
        
        //找到了一个可以被唤醒的节点并且不为NULL
        if (s != null)
            //调用LockSupport的unpark将其唤醒。
            LockSupport.unpark(s.thread);
    }

5.唤醒后的逻辑AQS.acquireQueued

    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;
                }
                
                /*
                 *  调用parkAndCheckInterrupt()是就会阻塞当前线程,当被唤醒时,
                 *  继续进入自旋尝试获取锁。
                 */
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
    
    
            if (failed)
                cancelAcquire(node);
        }
    }

6.响应中断出队逻辑AQS.cancelAcquire

	   /*
		*  取消指定node参与竞争 
     	*/
	private void cancelAcquire(Node node) {
    
    
		// 判空
        if (node == null)
            return;
		
        //取消node排队,直接将内部关联的线程置为NULL
        node.thread = null;

        //获取当前取消排队node的前驱
        Node pred = node.prev;
        //有可能它的前驱也处于取消状态,继续往前找 找到一个正常的Node。
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

		
        /*
         *  拿到前驱节点的next节点 这里有两种情况
         *   1.就是当前node
         *   2.可能也是ws > 0的节点
         */
        Node predNext = pred.next;

		//设置当前节点的状态为 取消状态
        node.waitStatus = Node.CANCELLED;

		
        /*
         *  当前取消排队的node所在的队列的位置不同,执行的出队的逻辑是一样的,一共分为三		 * 	种情况
         *  1.当前node是队尾,tail -> node
         *  2.当前node非head.next节点,也不是tail 
         *  3.当前node是head.next节点
         */
        
        
        /*
         * CASE1
         * ndoe == tail 成立:当前node是队尾,tail ——> node
         * 此时使用CAS方式将tail指向node的前驱节点,
         * 并将前驱节点的next置为NULL(将node出队)
         */
        if (node == tail && compareAndSetTail(node, pred)) {
    
    
            compareAndSetNext(pred, predNext, null);
            
        } else {
    
    
			
            
            //保存节点的状态(waitStatus)
            int ws;
            
     /*
      * 这一堆判断判断的就是node非head.next也不是tail节点的情况。
      * pred != head成立,说明当前node不是head.next节点,也不是tail(CASE1)
      * 条件二:((ws = pred.waitStatus) == Node.SIGNAL ||
               (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL)))
      * 2.1 成立:表示当前ndoe节点的前驱状态时signal,不成立:前驱状态可能为0,极端情况		  *  下:前驱节点也取消
      * 2.2 成立:ws <= 0 则需要设置前驱节点状态为signal状态,表示要唤醒后继节点
      * if里面做的事,就是让pred.next -> node.next,所以需要保证pred节点状态为signal。
      */
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
               (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
    
    
                
                
                /*
                 *  当前node不是head.next节点,也不是tail节点
                 *  出队: pred.next -> node.next节点后,当node.next节点被唤醒后,
                 *  调用shouldParkAfterFailedAcquire会让node.next越过取消状态的节				  *  点,完成真正的出队
                 */                
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
    
    
                //第三种情况,node是head.next节点,唤醒node的后继节点,然后调用
                //类似情况二,后继节点唤醒后会调用调用shouldParkAfterFailedAcquire让node.next越过取消状态的节点,与head建立双重指向的关系。
                unparkSuccessor(node);
            }

            node.next = node; // help GC
        }
    }

猜你喜欢

转载自blog.csdn.net/qq_46312987/article/details/121696539