【问题标题】:How can I interrupt a thread/future/executor with reason in Java?如何在 Java 中以原因中断线程/未来/执行程序?
【发布时间】:2019-11-14 14:22:20
【问题描述】:

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

现在我可以通过

中断一个线程
Thread.interrupt()

通过

取消未来
Future.cancel(boolean mayInterruptIfRunning)

通过

关闭执行器
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();
    }
}

【问题讨论】:

  • 你到底想对这个原因做什么?
  • 在大多数情况下,只需记录它。如果线程被预期的原因中断 - 不需要更多操作,但如果线程被任何其他原因中断,那么我想知道它并记录那次事故
  • 你可以控制线程的创建吗?
  • 是的,我可以完全控制线程的创建
  • 答案是否定的。一个线程可以被中断,但是没有这样的api来获取原因。您必须查看中断的代码。

标签: java multithreading interrupt


【解决方案1】:

您必须为此编写自定义 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
        }
    }
}

【讨论】:

  • 如果您有任务在循环中运行,则带有标志的解决方案很好。然后正如您所说,您可以防止进一步的迭代运行。在我的情况下,一个运行时间很长的单个任务可以随时被立即中断
  • 实现 Runnable 的解决方案不是最好的,因为 Runnable 不能抛出异常。使用 Callable 可以解决这个问题。
  • 然后通过存储对来自run 的执行线程的引用来继续上述操作。在interrupt 方法中终止它。我想这应该适用于AtomicReference
  • Runnable 只是一个关于如何实现接口的示例。你可以选择任何实现Interruptable的类。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-09-03
  • 2017-09-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多