美文网首页
1.8AQS(AbstractQueuedSynchronize

1.8AQS(AbstractQueuedSynchronize

作者: IT界刘德华 | 来源:发表于2019-11-02 09:36 被阅读0次

AQS类继承关系图

因为AQS类本身都是空方法,要使用必须自己去继承实现

AbstractOwnableSynchronizer类结构

AQS的父类,对独占模式持有同步锁进行定义、获取、设置

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

    /** Use serial ID even though all fields transient. */
    private static final long serialVersionUID = 3737899427754241961L;

    /**
     * Empty constructor for use by subclasses.
     */
    protected AbstractOwnableSynchronizer() { }

    /**
     * 独占模式同步的当前所有者线程
     */
    private transient Thread exclusiveOwnerThread;

    /**
     * 设置 独占模式同步的当前所有者线程
     */
    protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
    }

    /**
     * 获取独占模式同步的当前所有者线程
     */
    protected final Thread getExclusiveOwnerThread() {
        return exclusiveOwnerThread;
    }
}

AbstractQueuedSynchronizer类结构

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

    private static final long serialVersionUID = 7373984972572414691L;


    protected AbstractQueuedSynchronizer() { }
    
    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上,当其他线程对Condition调用了signal()方法后,该节点从等待队列中转移到同步队列中,加入到对同步状态的获取中 */
        static final int CONDITION = -2;
        /**
         * 表示下一个被默认对象的等待状态值应该无条件传播,就是共享模式需要对其它对象进行唤醒
         */
        static final int PROPAGATE = -3;

        /**
         *1、CANCELLED,值为1 。场景:当该线程等待超时或者被中断,需要从同步队列中取消等待,则该线程被置1,即被取消(这里该线程在取消之前是等待状态)。节点进入了取消状态则不再变化;
         *2、SIGNAL,值为-1。场景:后继的节点处于等待状态,当前节点的线程如果释放了同步状态或者被取消(当前节点状态置为-1),将会通知后继节点,使后继节点的线程得以运行;
         *3、CONDITION,值为-2。场景:节点处于等待队列中,节点线程等待在Condition上,当其他线程对Condition调用了signal()方法后,该节点从等待队列中转移到同步队列中,
         *  加入到对同步状态的获取中;
         *4、PROPAGATE,值为-3。场景:表示下一次的共享状态会被无条件的传播下去;
         *5、INITIAL,值为0,初始状态
         */
        volatile int waitStatus;

        /**
         * 前驱节点,当节点加入同步队列的时候被设置(尾部添加)
         */
        volatile Node prev;

        /**
         * 后继节点
         */
        volatile Node next;

        /**
         * 获取同步状态的线程
         */
        volatile Thread thread;

        /**
         * 等待节点的后继节点。如果当前节点是共享的,那么这个字段是一个SHARED常量,也就是说节点类型(独占和共享)和等待队列中的后继节点共用一个字段。
         *(注:比如说当前节点A是共享的,那么它的这个字段是shared,也就是说在这个等待队列中,A节点的后继节点也是shared。如果A节点不是共享的,
         *那么它的nextWaiter就不是一个SHARED常量,即是独占的。)
         */
        Node nextWaiter;

        /**
         * 判断是否是共享节点.
         */
        final boolean isShared() {
            return nextWaiter == SHARED;
        }

        /**
         * 返回前一个节点,如果为空则抛出NullPointerException。当前任不能为空时使用。可以省略null检查,但它是用来帮助VM的。
         *
         */
        final Node predecessor() throws NullPointerException {
            Node p = prev;
            if (p == null)
                throw new NullPointerException();
            else
                return p;
        }

        Node() {    // Used to establish initial head or SHARED marker
        }

        //初始化node,如果是独占模式,初始化时nextWaiter会指向null,共享模式就指向SHARED
        Node(Thread thread, Node mode) {     // Used by addWaiter
            this.nextWaiter = mode;
            this.thread = thread;
        }

        Node(Thread thread, int waitStatus) { // Used by Condition
            this.waitStatus = waitStatus;
            this.thread = thread;
        }
    }

    //指向头结点的指针
    private transient volatile Node head;

   //指向尾结点的指针
    private transient volatile Node tail;

    //状态,可以表示重入锁的重入次数
    private volatile int state;
}
acquire(独占锁,获取资源)
//获取资源,如果tryAcquire没获取到资源,则acquireQueued从等待队列自旋去获取资源
//如果依然没获取到则进入阻塞状态,当然如果acquireQueued在判断取到的线程已经被中断
//则调用selfInterrupt去打断线程,当然你中断归中断,我抢锁还是照样抢锁,几乎没关系,只是我抢到锁了以
//后,设置线程的中断状态而已,也不抛出任何异常出来。调用者获取锁后,可以去检查是否发生过中断,也//可以不理会
//参数arg,该值会传送到{@link #tryAcquire},但否则不会被解释,可以代表任何内容,如reentrantLock传的是1。
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            //则调用selfInterrupt去打断线程,当然你中断归中断,我抢锁还是照样抢锁,几乎没关系,只是我抢到锁了以
            //后,设置线程的中断状态而已,也不抛出任何异常出来。调用者获取锁后,可以去检查是否发生过中断,也//可以不理会
            selfInterrupt();
    }
  • tryAcquire(独占)
    尝试获取资源,这里只是进行定义,在继承AbstractQueuedSynchronizer类的类中重写
protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
  • addWaiter(为当前线程和给定模式创建和排队节点)
//添加或创建排队队列
//mode代表的是模式如:Node.EXCLUSIVE-独占模式,Node.SHARED-共享模式
private Node addWaiter(Node mode) {
        //新建与一个当前线程关联的node
        Node node = new Node(Thread.currentThread(), mode);
        //新建节点的前指针指向队列中的尾节点
        Node pred = tail;
        //尾结点不为空的话
        if (pred != null) {
            //确定尾结点不为空后,新建节点的前指针指向尾结点
            node.prev = pred;
             //cas比较将新节点设置到队列尾结点
             //根据当前对象AbstractQueuedSynchronized+tailOffset(尾结点的偏移量,也就是汇编中的寻址地址)找到内存中的Node节点
             //然后用代码中的尾结点和找到内存的Node做比较,如果一样则将欲插入的节点更新当前内存中的尾结点
             //保证线程安全
            if (compareAndSetTail(pred, node)) {
                //当前队列尾结点的next指向当前node,也就是将当前node加入等待队列
                pred.next = node;
                //返回当前新建节点
                return node;
            }
        }
        //如果cas比较设置尾结点失败后,则通过enq自旋直到加入成功为止
        enq(node);
        //加入成功后返回当前新建节点
        return node;
    }
  • enq方法
//不断自旋,直到插入出错的节点入队列为止
private Node enq(final Node node) {
        for (;;) {
            //队列原尾结点
            Node t = tail;
            //尾结点为空,正常队列有头节点,尾结点就会指向头节点
            //而尾节点为空,代表头结点应该也是不存在
            //所以下方会新建节点去比较设置头节点
            if (t == null) { 
                //创建新节点cas比较创建头结点
                if (compareAndSetHead(new Node()))
                    //创建好头结点后,要让尾结点指向头结点
                    tail = head;
            } else {
                //当完成第一个if初始化后,则在这边加入新的节点到队列
                //假如本来已经存在头结点,就会直接走else添加节点到队列
                
                //将新节点前指针指向队列尾结点
                node.prev = t;
                //cas比较将新节点设置到队列尾结点
                //根据当前对象AbstractQueuedSynchronized+tailOffset(尾结点的偏移量,也就是汇编中的寻址地址)找到内存中的Node节点
                //然后用代码中的尾结点和找到内存的Node做比较,如果一样则将欲插入的节点更新当前内存中的尾结点
                //保证线程安全
                if (compareAndSetTail(t, node)) {
                    //将当前队列的尾结点的next指针指向欲插入的node节点
                    t.next = node;
                    //返回当前节点
                    return t;
                }
            }
        }
    }
  • compareAndSetTail
//调用Unsafe类的cas方法比较设置尾结点
private final boolean compareAndSetTail(Node expect, Node update) {
        //this代表当前的AbstractQueuedSynchronizer类对象
        //tailOffset代表尾结点在内存中偏移量,也就是寻址地址
        //expect是指期望的节点
        //update是要更新的节点
        //方法的意思是用当前对象加上尾结点的偏移量找到内存中的尾结点,然后用找到的尾结点和expect(期望的尾结点,或者是拿到的尾结点)节点作比较
        //如果一样,就将当前要加入的新节点设置为新的尾结点并返回true
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }
  • 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);
                    //前置节点的next指针也置为空,帮助GC
                    p.next = null; 
                    failed = false;
                    //返回中断状态
                    return interrupted;
                }
                //老二没获取到资源,或者当前节点还不是老二
                //则执行下面程序
                //shouldParkAfterFailedAcquire对当前前置节点的状态检查是否是SIGNAL
                //不是返回false,通过当前for循环去更新其前置节点并设置前置节点状态为SIGNAL
                //当前置节点为SIGNAL
                //则调用parkAndCheckInterrupt进行阻塞了,就停止在这句代码
                //当前值节点释放资源后因为有SIGNAL状态,会去通知当前节点结束阻塞竞争资源
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //到这里中断状态改为true,当然只是改了下状态,并没有直接结束方法,而且第二再掉中断会变成false状态
                  //所以在selfInterrupt();要再进行一次中断记录这次是被中断的
                    interrupted = true;
            }
        } finally {
            //如果获取失败,一般是中断抛出异常进入,这里基本不会
            if (failed)
                //取消资源的获取
                cancelAcquire(node);
        }
    }
  • setHead设置头结点
//将当前node设置为头结点
private void setHead(Node node) {
        //将头节点指针指向当前节点,也就是当前节点设置为头节点
        head = node;
        //将该节点在等待队列的线程置为空,释放当前线程
        node.thread = null;
        //将当前节点前一节点也置为空
        node.prev = null;
    }
  • shouldParkAfterFailedAcquire
    这个方法主要是确定当前节点的前置节点是否是SIGNAL状态
    如果前置节点取消了等待,则为他匹配新的前置节点
    然后等下一次for循环调用shouldParkAfterFailedAcquire的时候确定前置节点置为SIGNAL再进入if (ws == Node.SIGNAL)返回true
//这个方法主要是确定当前节点的前置节点是否是SIGNAL状态
//如果前置节点取消了等待,则为他匹配新的前置节点
//然后等下一次for循环调用shouldParkAfterFailedAcquire的时候确定前置节点置为SIGNAL再进入if (ws == Node.SIGNAL)返回true
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        //获取当前节点的前置节点的状态
        //CANCELLED =  1;
        //SIGNAL = -1;
        //CONDITION = -2;
        //PROPAGATE = -3;
        int ws = pred.waitStatus;
        //如果前置节点的状态已经是SIGNAL,也就是当前置节点释放资源的时候,要提醒当前节点去获取资源
        if (ws == Node.SIGNAL)
            //就直接返回true
            return true;
        //如果ws>0,则前置节点的状态CANCELLED,取消状态
        //为当前节点重新匹配新的前置节点,因为取消的前置节点已经不会再去通知当前节点了
        if (ws > 0) {
            
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            //如果前置节点的状态不是取消的状态,则用cas将前一个节点设置为SIGNAL
            //当前置节点释放资源的时候,要提醒当前节点去获取资源
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }   
}
  • compareAndSetWaitStatus方法
//比较并设置等待状态值
private static final boolean compareAndSetWaitStatus(Node node,
                                                         int expect,
                                                         int update) {
        //调用Unsafe的compareAndSwapInt方法比较并设置状态
        //前一个node节点+等待状态的偏移量找到内存中的等待状态
        //然后和现在前一个状态做比较,如果一样,可以对内存中的状态值用新值update进行替换
        return unsafe.compareAndSwapInt(node, waitStatusOffset,
                                        expect, update);
  • parkAndCheckInterrupt线程挂起并检查是否中断
//当前一个方法shouldParkAfterFailedAcquire已经将前置节点设置为SIGNAL状态
//那当前节点就可以安心休息了
private final boolean parkAndCheckInterrupt() {
        //线程挂起,阻塞在这
        LockSupport.park(this);
        //线程是否中断
        return Thread.interrupted();
    }
  • LockSupport.park线程挂起,并记录线程阻塞对象和对应的偏移量
//线程挂起
public static void park(Object blocker) {
        Thread t = Thread.currentThread();
        //在调用park阻塞当前线程之前,先记录当前线程的blocker
        setBlocker(t, blocker);
        //调用park阻塞当前线程
        //UNSAFE.park的两个参数,前一个为true的时候表示传入的是绝对时间,
        //false表示相对时间,即从当前时间开始算。
        //后面的long类型的参数就是等待的时间,0L表示永久等待
        UNSAFE.park(false, 0L);
        //阻塞结束后让线程继续执行下去的情况时,再将parkBlocker设置为null,
        //因为当前线程已经没有被blocker住了,如果不设置为null,
        //那诊断工具获取被阻塞的原因就是错误的,这也是为什么要有两个setBlocker的原因
        setBlocker(t, null);
    }   
  • cancelAcquire(取消节点获取资源)
//取消节点获取资源
private void cancelAcquire(Node node) {
        // 忽略空节点
        if (node == null)
            return;

        //将当前node维护的线程释放
        node.thread = null;

        // 跳过取消状态的前置节点,找到非取消状态的前置节点作为当前节点的前置节点
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        //新找到的前置节点的后置节点
        Node predNext = pred.next;

        //当前节点状态标注为取消状态
        node.waitStatus = Node.CANCELLED;

        // 当前节点已经是尾结点
        //并且cas比较设置新的尾结点成功(新的尾结点就是当前节点的前置节点,也就是将当前node移除了)
        if (node == tail && compareAndSetTail(node, pred)) {
            //设置前置节点的下一个节点为空,到这一步已经将当前节点移除等待队列
            compareAndSetNext(pred, predNext, null);
        //如果不是尾结点或者cas比较没成功
        } else {
            
            int ws;
            //前置节点不是头结点
            //前置节点是SIGNAL或者(前置节点不是取消状态并且比较设置前置接的状态为SIGNAL成功)
            //前置节点维护的线程没被释放
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                //进入这个if说明当前节点的前置不是头节点并且是SIGNAL状态,有维护线程
                
                //当前节点的后继节点
                Node next = node.next;
                //后继节点存在并且不是cancel状态
                if (next != null && next.waitStatus <= 0)
                    //直接比较设置前置节点的后继节点为当前节点的后继节点
                    //也就是移除了当前节点,让前置直接指向了后继节点
                    compareAndSetNext(pred, predNext, next);
            //如果是头结点,或是别的状态进入else
            } else {
                //将当前节点状态设置为初始化状态0,释放当前节点的下一个节点
                unparkSuccessor(node);
            }

            
            node.next = node; // help GC
        }
    }
  • compareAndSetTail(通过cas比较设置尾结点)
//当前AbstractQueuedSynchronizer类+尾结点偏移量找到内存中的尾结点
//用当前期望的尾结点和内存的尾结点比较,如果一样,将设置新的尾结点
private final boolean compareAndSetTail(Node expect, Node update) {
        return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
    }
  • compareAndSetNext(通过cas比较设置next指针指向的节点)
/**
     * 用当前node+下一个节点的偏移量得到内存中的节点,然后用期望节点和内存的节点进行比较,一样用update更新节点
     */
    private static final boolean compareAndSetNext(Node node,
                                                   Node expect,
                                                   Node update) {
        return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
    }
  • unparkSuccessor取消线程的阻塞状态
    当执行LockSupport.unpark(s.thread)方法后,该线程会去执行阻塞在 if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())的parkAndCheckInterrupt方法下的其它代码,当唤醒了线程则它会去确认自己是不是老二,是的话就尝试获取锁,获取到了就将自己变成了头结点
//取消节点的阻塞状态
private void unparkSuccessor(Node node) {
        //获取当前node的状态
        int ws = node.waitStatus;
        //node的状态不是取消状态则用cas比较设置状态为0初始化状态
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        //node的后继节点
        Node s = node.next;
        
        //如果后继节点不存在或者后继节点已经是cancel放弃状态
        if (s == null || s.waitStatus > 0) {
            //如果s!=null,状态是cance状态则,将后继节点置空,帮助GC回收
            s = null;
            //从尾结点开始,尾结点不是空,一直往前遍历
            //找到状态不是取消状态的节点,作为node的真正后继节点
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        //后继节点不是空就释放后继线程
        if (s != null)
            LockSupport.unpark(s.thread);
    }
acquireShared获取资源(共享锁)
//获取共享锁
public final void acquireShared(int arg) {
        //获取到的状态>=0不会去执行获取资源(已经获取到资源),当然tryAcquireShared需要自己实现
        if (tryAcquireShared(arg) < 0)
            //获取共享锁
            doAcquireShared(arg);
    }
  • doAcquireShared判断自己是老二嘛,是老二获取锁,释放资源后并通知其它共享节点,直到所有节点被唤醒
private void doAcquireShared(int arg) {
        //获得一个共享模式与当前线程关联的节点
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                //当前节点的前驱节点
                final Node p = node.predecessor();
                //前驱节点是头结点
                if (p == head) {
                    //当前节点尝试获取资源
                    int r = tryAcquireShared(arg);
                    //获取到了资源
                    if (r >= 0) {
                        //将自己设为头结点
                        //并唤醒其它共享节点
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        //已经中断,则自我中断设置状态为true,记录本次中断状态
                        if (interrupted)
                            selfInterrupt();
                        failed = false;
                        return;
                    }
                }
                //老二没获取到资源,或者当前节点还不是老二
                //则执行下面程序
                //shouldParkAfterFailedAcquire对当前前置节点的状态检查是否是SIGNAL
                //不是返回false,通过当前for循环去更新其前置节点并设置前置节点状态为SIGNAL
                //当前置节点为SIGNAL
                //则调用parkAndCheckInterrupt进行阻塞了,就停止在这句代码
                //当前值节点释放资源后因为有SIGNAL状态,会去通知当前节点结束阻塞竞争资源
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            //分析与独占的一样,就不多说了
            if (failed)
                cancelAcquire(node);
        }
    }
  • setHeadAndPropagate设置头结点并且传递共享
//将自己设为头结点
//并唤醒其它共享节点
private void setHeadAndPropagate(Node node, int propagate) {
        //当前头结点暂存
        Node h = head; 
        //将当前节点设为新的头节点
        setHead(node);
        
        //propagate>0允许传播
        //旧的头结点是空
        //旧的头结点不是空但是旧的头结点的状态不是取消和初始化状态
        //新的头结点是空
        //新的头结点的状态不是取消和初始化状态
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            //当前节点的后继节点
            Node s = node.next;
            //后继节点是空或者后继节点是共享节点
            if (s == null || s.isShared())
                //唤醒其它共享节点
                doReleaseShared();
        }
    }
  • doReleaseShared更改自己的状态为PROPAGATE,并且唤醒其它共享节点
//判断自己是SIGNAL状态,是的话,有后继节点需要去唤醒
//调用unparkSuccessor唤醒它后继节点,唤醒后,后继节点从阻塞状态中醒来
//回到parkAndCheckInterrupt方法下的其它代码,去确认自己是不是老二了,
//是老二则去竞争老大竞争成功后,当然head改变了
//head变成自己的后继节点,然后又开始循环,直到没有其它需要唤醒的节点停止
private void doReleaseShared() {
        
        for (;;) {
            Node h = head;
            //头结点存在,且后面还有节点,也就是队列里的节点不止一个
            if (h != null && h != tail) {
                
                int ws = h.waitStatus;
                //头结点的状态是SIGNAL,说明释放它自己后需要去唤醒它的下一个节点
                if (ws == Node.SIGNAL) {
                    //cas比较设置状态为0,因为要释放节点
                    //如果设置成功,执行unparkSuccessor唤醒下一个节点
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //当执行LockSupport.unpark(s.thread)方法后,
                    //该线程会去执行阻塞在 
                    //if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())
                    //的parkAndCheckInterrupt方法下的其它代码,
                    //当唤醒了线程则它会去确认自己是不是老二,是的话就尝试获取锁,
                    //获取到了就将自己变成了头结点
                    unparkSuccessor(h);
                }
                //当头结点的状态已经是初始化状态了,cas尝试将自己状态改完共享确保以后可以传递下去
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            //头结点和当前节点一样,停止
            //也就是头结点已经被其它人获取到了则还会继续循环,说明还有其它节点需要唤醒
            //当头结点和h一样说明头结点的下一个节点并没有变成老大,也就是没有唤醒的了,结束
            if (h == head)                   
                break;
        }
    }   
acquireSharedInterruptibly获取资源,可中断
//请求资源,可中断
public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //是否已被中断,是的话就抛出异常直接返回了,不再执行方法
        if (Thread.interrupted())
            throw new InterruptedException();
        //尝试获取资源,没获取到就进入doAcquireSharedInterruptibly
        if (tryAcquireShared(arg) < 0)
            //获取资源,如果被状态则取消获取资源
            doAcquireSharedInterruptibly(arg);
    }
  • interrupted
    判断线程是否被中断,true代表会清除中断状态,假如第一次中断状态是true
    但是第一次调用完isInterrupted(true)则会清除中断状态,第二次调用中断状态就变成了false
//判断线程是否被中断,true代表会清除中断状态,假如第一次中断状态是true
//但是第一次调用完isInterrupted(true)则会清除中断状态,第二次调用中断状态就变成了false
public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }   
  • doAcquireSharedInterruptibly
    获取资源,如果被状态则取消获取资源
//获取资源,如果被状态则取消获取资源
private void doAcquireSharedInterruptibly(int arg)
       throws InterruptedException {
       //添加共享节点到等待队列
       final Node node = addWaiter(Node.SHARED);
       boolean failed = true;
       try {
           for (;;) {
               //当前节点的前驱节点
               final Node p = node.predecessor();
               
               if (p == head) {
                   int r = tryAcquireShared(arg);
                   //获取到资源
                   if (r >= 0) {
                       //设置头结点并且唤醒其它节点
                       setHeadAndPropagate(node, r);
                       p.next = null; // help GC
                       failed = false;
                       return;
                   }
               }
               //当前节点的前驱节点不是头结点,则找到正确的前驱节点并标注状态为SIGANAL
               //然后进入阻塞状态
               //当阻塞状态被唤醒并且是被中断的则会抛出异常,直接结束方法
               //当然抛出异常后也会走finally,也就是failed默认true直接去取消获取资源
               if (shouldParkAfterFailedAcquire(p, node) &&
                   parkAndCheckInterrupt())
                   throw new InterruptedException();
           }
       } finally {
           if (failed)
               cancelAcquire(node);
       }
   }
tryRelease(独占),需要重写
protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
tryReleaseShared(共享),需要重写
protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

理解的有啥不正确大家可以指出哦,喜欢的点个赞!!!!

相关文章

网友评论

      本文标题:1.8AQS(AbstractQueuedSynchronize

      本文链接:https://www.haomeiwen.com/subject/zvypvctx.html