AQS独占锁取消排队与线程中断

 大家推荐个靠谱的公众号程序员探索之路,公众号内点击网赚获取彩蛋,大家一起加油,这个公众号已经接入图灵https://img-blog.csdnimg.cn/20181129224604602.png ​ 

1.AQS独占锁的取消排队
    //这个方法是 自旋获取锁/获取不到锁线程挂起等待唤醒
    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;
                }
                //分析这个if语句  第一个条件把node的前驱节点的waitStatus置为-1 也就是signal状态  如果这一步成功了 那么会进行parkAndCheckInterrupt方法的判断
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

    /**
     * Convenience method to park and then check if interrupted
     *
     * @return {@code true} if interrupted
     * //可以看出这个方法就是让线程挂起  然后线程唤醒之后 会返回线程的中断状态 和前面的if语句结合 shouldParkAfterFailedAcquire(p, node) parkAndCheckInterrupt() 都返回true会把interrupted 置为true 然后还是会抢锁
     * //返回最外层看看
     */
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

    lock方法的外层
    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

    可以看到 tryAcquire
    和 acquireQueued
    也就是抢锁失败 然后线程挂起, 唤醒后如果中断了还是会抢锁

    最后 会调用interrupt()方法

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

    await方法的外层
        if(

    acquireQueued(node, savedState) &&interruptMode !=THROW_IE)
    interruptMode =REINTERRUPT;
            if(node.nextWaiter !=null) // clean up if cancelled

    unlinkCancelledWaiters();
            if(interruptMode !=0)

    reportInterruptAfterWait(interruptMode);

    可以看到 第一个if语句
    如果中断而且不是signal 之前中断
    interruptMode =REINTERRUPT;
    最后一个if语句 会处理中断
    抛出中断异常或者调用interrupt方法

    到这里可以看到即使中断了 还是会继续抢锁

    别急往下看 看另外一个lock方法

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }

    //有了之前的介绍 这个方法几步多说了
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }

    /**
     * Acquires in exclusive interruptible mode.
     *
     * @param arg the acquire argument
     *            可以看到 shouldParkAfterFailedAcquire和parkAndCheckInterrupt都是true的话 会直接抛出InterruptedException异常
     *            然后会在finally里cancelAcquire方法
     *            而且外层也不会处理 直接抛出 那么
     */
    private void doAcquireInterruptibly(int arg)
            throws InterruptedException {
        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;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

    /**
     * Cancels an ongoing attempt to acquire.
     *
     * @param node the node
     *             这个就是如何取消队列本尊了
     */
    private void cancelAcquire(Node node) {
        // Ignore if node doesn't exist
        if (node == null)
            return;

        node.thread = null;

        // Skip cancelled predecessors
        //跳过取消的节点 一直往前找waitStatus<=0的节点
        Node pred = node.prev;
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;

        // predNext is the apparent node to unsplice. CASes below will
        // fail if not, in which case, we lost race vs another cancel
        // or signal, so no further action is necessary.
        Node predNext = pred.next;

        // Can use unconditional write instead of CAS here.
        // After this atomic step, other Nodes can skip past us.
        // Before, we are free of interference from other threads.
        node.waitStatus = Node.CANCELLED;

        // If we are the tail, remove ourselves.
        //如果node是末尾节点  而且设置node的前驱节点为等待队列的末尾节点成功 那就设置 node的前驱节点的下一个节点为null
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        } else {
            // If successor needs signal, try to set pred's next-link
            // so it will get one. Otherwise wake it up to propagate.
            //如果node的pred不是head 而且perd的waitStatus能设置为signal 那么就把pred.next = node.next也就是 把node去掉
            int ws;
            if (pred != head &&
                    ((ws = pred.waitStatus) == Node.SIGNAL ||
                            (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                    pred.thread != null) {
                Node next = node.next;
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            } else {
                unparkSuccessor(node);
            }

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

    2.下面来介绍一下线程中断
    线程中断不是类似linux里面的kil -9pid ,也就是说线程中断了,就立即停止了.中断代表的是线程的状态(还有其他含义),
    初始值是false 
     Thread实例的方法

    //获取 线程的中断状态 不清楚
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    /**
     * Tests if some Thread has been interrupted.  The interrupted state
     * is reset or not based on the value of ClearInterrupted that is
     * passed.
     * 看解释  如果参数为true就会清楚线程的中断状态 也就是重置为false
     */
    private native boolean isInterrupted(boolean ClearInterrupted);

    用于设置一个线程的中断状态为 true

    public void interrupt() {
    }

    // Thread 中的静态方法,检测调用这个方法的线程是否已经中断
// 注意:这个方法返回中断状态的同时,会将此线程的中断状态重置为 false
// 所以,如果我们连续调用两次这个方法的话,第二次的返回值肯定就是 false 了
    public static boolean interrupted() {
    }

    对于一些方法 线程中断了
    可以感知到 例如LockSupport.park(this);

    线程挂起后 如果调用xxthread.interrupt()方法,线程会被唤醒 然后响不响应中断是自己的事

    例如上面的两种lock方法 一种响应抛出InterruptedException异常, 另外一种只是位置线程的中断状态

    列举一些能感知线程被中断的方法
1.
    来自 Object类的 wait()、wait(long)、 wait(long, int),来自 Thread类的 join()、join(long)、join(long, int)、sleep(long)、sleep(long, int)

    这几个方法的相同之处是,方法上都有:throws InterruptedException

2.如果线程阻塞在这些方法上(我们知道,这些方法会让当前线程阻塞),这个时候如果其他线程对这个线程进行了中断,那么这个线程会从这些方法中立即返回,
    抛出 InterruptedException
    异常,同时重置中断状态为 false。

    实现了 InterruptibleChannel
    接口的类中的一些 I/O 阻塞操作,如 DatagramChannel中的 connect方法和 receive方法等

    如果线程阻塞在这里,中断线程会导致这些方法抛出 ClosedByInterruptException并重置中断状态。
     3.Selector 中的select 方法, 这个有机会我们在讲 NIO的时候说一旦中断,方法立即返回
    对于以上 3种情况是最特殊的,因为他们能自动感知到中断(这里说自动,当然也是基于底层实现),并且在做出相应的操作后都会重置中断状态为 false。

    InterruptedException 概述
    它是一个特殊的异常,
    不是说 JVM
    对其有特殊的处理,而是它的使用场景比较特殊。通常,我们可以看到,
    像 Object中的 wait() 方法,ReentrantLock 中的lockInterruptibly() 方法,
    Thread 中的sleep() 方法等等,这些方法都带有 throws InterruptedException,我们通常称这些方法为阻塞方法(
    blocking method)。
    阻塞方法一个很明显的特征是,它们需要花费比较长的时间(不是绝对的,只是说明时间不可控),还有它们的方法结束返回往往依赖于外部条件,
    如 wait
    方法依赖于其他线程的 notify,
    lock 方法依赖于其他线程的
    unlock等等。
    当我们看到方法上带有 throws
    InterruptedException 时,我们就要知道,这个方法应该是阻塞方法,我们如果希望它能早点返回的话,我们往往可以通过中断来实现。
    除了几个特殊类(
    如 Object,Thread等)外,感知中断并提前返回是通过轮询中断状态来实现的。我们自己需要写可中断的方法的时候,就是通过在合适的时机(通常在循环的开始处)去判断线程的中断状态,
    然后做相应的操作(通常是方法直接返回或者抛出异常)。当然,我们也要看到,如果我们一次循环花的时间比较长的话,那么就需要比较长的时间才能注意到线程中断了。

    怎么处理中断 可以参考AQS怎么做的

    lock方法 这个不方法不响应中断但是也没有吧中断信息给丢掉
    在最外层 还是把线程的中断状态给设置为true
    自己写代码时可以自行检测

    public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }

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

    lockInterruptibly 方法
    上面介绍过 这里就不详细介绍了
    该方法 检测到中断
    直接抛出中断异常 而且不做处理
    向外层抛出

    在java的并发包中 对于中断的响应都是分为两类
    响应不响应,不响应也不会把中断信息丢掉,记录状态
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值