Java 中 Thread 的中断方法

Java juc 面试 大约 5840 字

interrupt()

给线程设置一个中断状态,线程仍会继续运行。

但如果线程正好处于等待状态(如:调用wait()sleep()join()方法),此时调用interrupt()方法,则线程立即被唤醒并抛出InterruptedException异常,然后继续执行sleep()后的代码。

public static void main(String[] args) throws InterruptedException {
    Thread.onSpinWait();
    Thread thread = new Thread(() -> {
        System.out.println(LocalDateTime.now() + " this is thread beginning..." + Thread.currentThread().getState());
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(LocalDateTime.now() + " this is thread ending..." + Thread.currentThread().getState());
    });
    thread.start();
    TimeUnit.SECONDS.sleep(1);
    thread.interrupt();
}

输出

2021-04-11T18:25:15.281534800 this is thread beginning...RUNNABLE
java.lang.InterruptedException: sleep interrupted
    at java.base/java.lang.Thread.sleep(Native Method)
    at java.base/java.lang.Thread.sleep(Thread.java:339)
    at java.base/java.util.concurrent.TimeUnit.sleep(TimeUnit.java:446)
    at thread.ThreadInterruptDemo.lambda$main$0(ThreadInterruptDemo.java:16)
    at java.base/java.lang.Thread.run(Thread.java:834)
2021-04-11T18:25:16.239082300 this is thread ending...RUNNABLE

isInterrupted()

测试此线程是否被中断,不清除中断状态。

Thread.interrupted()

静态方法。

测试调用此方法的线程是否被中断,清除中断状态标志位并返回true。第二次调用因为第一次已经清除了中断状态标志位,故返回false

public static void main(String[] args) {
    Thread.onSpinWait();
    Thread thread = new Thread(() -> {
        Thread currentThread = Thread.currentThread();
        System.out.println(LocalDateTime.now() + " this is thread beginning..." + currentThread.getState());
        currentThread.interrupt();
        System.out.println("isInterrupted1 = " + currentThread.isInterrupted());
        System.out.println("isInterrupted2 = " + currentThread.isInterrupted());
        System.out.println("interrupted1 = " + Thread.interrupted());
        System.out.println("interrupted2 = " + Thread.interrupted());
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(LocalDateTime.now() + " this is thread ending..." + currentThread.getState());
    });
    thread.start();
}

输出:

2021-04-11T20:14:44.122852600 this is thread beginning...RUNNABLE
isInterrupted1 = true
isInterrupted2 = true
interrupted1 = true
interrupted2 = false
2021-04-11T20:14:54.176544400 this is thread ending...RUNNABLE

Thread.yield()

调用Thread.yield后,线程让出CPUCPU调度优先级比当前线程更高或者优先级相等的线程,也有可能继续调度到自己线程,继续执行后续代码。

在并发场景下,可以提高CPU利用率。

源码分析

public class Thread implements Runnable {

    // 该对象包含在一个线程中,该线程被一个可中断的 I/O 操作阻塞
    // blocker 对象的中断方法将在被设置该线程的中断状态后调用
    private volatile Interruptible blocker;
    // 对象锁
    private final Object blockerLock = new Object();

    // 设置一个中断状态
    public void interrupt() {
        // 如果不在运行线程中调用
        if (this != Thread.currentThread()) {
            // 检测是否有权限,通过所属线程组判断,没权限抛出异常
            checkAccess();

            // 线程可能被 I/O 操作阻塞
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if (b != null) {
                    interrupt0();  // 设置中断状态标志位
                    b.interrupt(this); // 调用中断方法
                    return;
                }
            }
        }

        // 如果是在运行线程中调用,直接设置中断状态标志位
        interrupt0();
    }

    // native 方法
    // 设置中断状态标志位
    private native void interrupt0();

    // 测试线程是否中断
    // 底层调用 Thread 私有的方法 isInterrupted(boolean)
    // false 表示不清除中断状态标志位
    public boolean isInterrupted() {
        return isInterrupted(false);
    }

    // 静态方法
    // 测试当前线程(Thread.interrupted() 在哪里调用就是测试的哪个线程)是否被中断
    // 底层调用 Thread 私有的方法 isInterrupted(boolean)
    // true 表示清除中断状态标志位
    public static boolean interrupted() {
        return currentThread().isInterrupted(true);
    }

    // native 方法
    // 测试线程是否被中断,根据 ClearInterrupted 参数决定是否重置中断状态标志位
    @HotSpotIntrinsicCandidate
    private native boolean isInterrupted(boolean ClearInterrupted);

    // 让出 CPU 执行权
    public static native void yield();

}

应用场景

FutureTask源码中在get()方法中,将等待任务插入链表中。

private int awaitDone(boolean timed, long nanos) throws InterruptedException {

    long startTime = 0L;    // Special value 0L means not yet parked
    WaitNode q = null;
    boolean queued = false;
    for (;;) {
        int s = state;
        if (s > COMPLETING) {
            if (q != null)
                q.thread = null;
            return s;
        }
        else if (s == COMPLETING)
            // We may have already promised (via isDone) that we are done
            // so never return empty-handed or throw InterruptedException
            Thread.yield();
        else if (Thread.interrupted()) {
            removeWaiter(q);
            throw new InterruptedException();
        }
        else if (q == null) {
            if (timed && nanos <= 0L)
                return s;
            q = new WaitNode();
        }
        else if (!queued)
            queued = WAITERS.weakCompareAndSet(this, q.next = waiters, q);
        else if (timed) {
            final long parkNanos;
            if (startTime == 0L) { // first time
                startTime = System.nanoTime();
                if (startTime == 0L)
                    startTime = 1L;
                parkNanos = nanos;
            } else {
                long elapsed = System.nanoTime() - startTime;
                if (elapsed >= nanos) {
                    removeWaiter(q);
                    return state;
                }
                parkNanos = nanos - elapsed;
            }
            // nanoTime may be slow; recheck before parking
            if (state < COMPLETING)
                LockSupport.parkNanos(this, parkNanos);
        }
        else
            LockSupport.park(this);
    }
}
阅读 507 · 发布于 2021-04-13

————        END        ————

扫描下方二维码关注公众号和小程序↓↓↓

扫描二维码关注我
昵称:
随便看看 换一批