首页 > 解决方案 > 如何在 Java 中以原因中断线程/未来/执行程序?

问题描述

我想知道为什么给定线程被中断。它是否被我(我的代码的一部分)、其他库或任何其他原因打断了?

现在我可以通过

Thread.interrupt()

取消未来

Future.cancel(boolean mayInterruptIfRunning)

通过以下方式关闭 Executor

ExecutorService.shutdown() or ExecutorService.shutdownNow()

但是,他们都没有给我发送原因的选项(执行中断的信息)。换句话说,我不能用更详细的消息或原因抛出和捕获 InterruptedException。

编辑:基于 Glains 解决方案,我准备了一个类似的解决方案,但使用了 Callable

public class MyCallable<T> implements Callable<T> {
    private final Callable<T> callable;

    private volatile Thread thread;
    private volatile String interruptReason;

    public MyCallable(final Callable<T> callable) {
        this.callable = callable;
        this.thread = null;
    }

    @Override
    public T call() throws Exception {
        this.thread = Thread.currentThread(); // not getCurrentThread()
        try {
            return callable.call();
        }
        catch (InterruptedException e) {
            System.out.println("Thread is interrupted: " + this.interruptReason);
        }
    }

    public void interrupt(String interruptReason) {
        this.interruptReason = interruptReason;
        thread.interrupt();
    }
}

标签: javamultithreadinginterrupt

解决方案


您必须为此编写自定义 API,因为Thread无法因特定原因中止。为了Runnable能够被打断,您可以创建一个提供此功能的自定义界面:

public interface Interruptable {

    default void interrupt() {
        interrupt(Reason.unknown());
    }

    void interrupt(Reason reason);
}

定义的原因可以是任何东西,但以下可能适合您的用例:

public class Reason {

    public static Reason unknown() {
        return new Reason("unknown");
    }

    private final String reason;

    public Reason(String reason) {
        this.reason = reason;
    }

    public String getReason() {
        return reason;
    }
}

现在,您将不得不调整您的Runnable实现,以便它可以被中断。请注意,以下解决方案优雅地中断了线程,这意味着当前迭代将完成,但不会开始新的迭代。如果要真正中断线程,则需要存储对执行Runnable实例run方法的线程的引用,因为interrupt不会从同一个线程调用该方法。然后,您可以调用Thread.interrupt该参考。

public class MyRunnable implements Runnable, Interruptable {

    private final AtomicBoolean running = new AtomicBoolean(false);

    @Override
    public void interrupt(Reason reason) {
        running.set(false);
        System.out.println("Thread is interrupted: " + reason.getReason());
    }

    @Override
    public void run() {
        running.set(true);
        while (running.get()) {
            // do work
        }
    }
}

推荐阅读