关于AQS(AbstractQueuedSynchronizer)中对中断的应用

来源:互联网 发布:中软国际java培训骗局 编辑:程序博客网 时间:2024/05/18 02:47

在阅读AbstractQueuedSynchronizer源码的过程中,发现了大量对中断机制的使用,正好最近看了一些Java中断机制的理论,这篇文档就是要看看Doug Lea是如何使用中断的。这篇文章是以前两篇文章为基础的:

尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(一)
尝试阅读ReentrantLock、AbstractQueuedSynchronizer源码(二)

一、谈谈对中断的理解

如何去中断一个正在执行的线程A?

方法1、直接终止线程A,不再运行
方法2、提出中断申请,线程A处理中断申请


对于方法1非常暴力,可能会导致数据的不一致性。同时也很不安全,就想我们借别人东西,首先要提出申请,能不能借到要看对方,不能直接去抢东西;
方法2就十分友好,线程A在收到中断申请后,只需在合适的时候处理中断即可,如果没有合适的时间点,甚至可以不处理。

如果A线程想中断B线程,可以通过interrupt()方法来通知B线程(每个线程对象中有一个标识位来标识是否接受到中断请求),B线程可以通过isInterrupted()、interrupt()来判断自己是否被申请中断。

interrupt():返回中断状态后,将中断状态清空

我们常见的Thread.sleep()等可阻塞方法是通过抛出InterruptedException来对中断申请做响应的

/**     * Causes the currently executing thread to sleep (temporarily cease     * execution) for the specified number of milliseconds, subject to     * the precision and accuracy of system timers and schedulers. The thread     * does not lose ownership of any monitors.     *     * @param  millis     *         the length of time to sleep in milliseconds     *     * @throws  IllegalArgumentException     *          if the value of {@code millis} is negative     *     * @throws  InterruptedException     *          if any thread has interrupted the current thread. The     *          <i>interrupted status</i> of the current thread is     *          cleared when this exception is thrown.     */    public static native void sleep(long millis) throws InterruptedException;

注释中说,如果有线程中断了当前线程,当抛出异常后,被中断线程的中断状态会被清空。

虽然Thread.sleep()是本地方法,不过可以预测内存形如:

if (this.interrupted()) {    ...    throw new InterruptedException();}

二、AQS中对中断的处理

acquire()/方法

在前两篇文章中我们详细分析了AQS的acquire()方法,该方法忽略了对中断的响应,但是会把中断标识向上传递,传递给调用者,调用者可自旋判断中断状态。

acquireInterruptibly()/方法

该方法对应中断请求的处理与前面说的Thread.sleep()类型,清空中断标识,抛出InterruptedException

public final void acquireInterruptibly(int arg)            throws InterruptedException {        if (Thread.interrupted())            throw new InterruptedException();        if (!tryAcquire(arg))            doAcquireInterruptibly(arg);    }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);        }    }

await()/方法

public final void await() throws InterruptedException {            if (Thread.interrupted())                throw new InterruptedException();            Node node = addConditionWaiter();            int savedState = fullyRelease(node);            int interruptMode = 0;            while (!isOnSyncQueue(node)) {                LockSupport.park(this);                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)                    break;            }            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)                interruptMode = REINTERRUPT;            if (node.nextWaiter != null) // clean up if cancelled                unlinkCancelledWaiters();            if (interruptMode != 0)                reportInterruptAfterWait(interruptMode);        }

这个方法就有点意思了,该方法对于中断的处理包括两种方式。第一种就是重新中断,不处理;另外一种就抛出InterruptedException

如果该线程已经被转移到了同步队列,则抛出InterruptedException;否则重新中断

// 只有线程处于中断状态,才会调用此方法final boolean transferAfterCancelledWait(Node node) {    // 用 CAS 将节点状态设置为 0     // 如果这步 CAS 成功,说明是 signal 方法之前发生的中断,因为如果 signal 先发生的话,signal 中会将 waitStatus 设置为 0    if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {        // 将节点转移到同步队列,即使中断了,依然会转移到阻塞队列        enq(node);        return true;    }    //  CAS 失败,因为 signal 方法已经将 waitStatus 设置为了 0    // signal 方法会将节点转移到同步队列,但是可能还没完成,这边自旋等待其完成    while (!isOnSyncQueue(node))        Thread.yield();    return false;}
阅读全文
1 0
原创粉丝点击