c2h50h
c2h50h

Reputation: 11

How can I interrupt a thread/future/executor with reason in Java?

I would like to know why a given thread has been interrupted. Was it interrupted by me (part of my code), by other library or by any other reason?

Right now I can interrupt a Thread by

Thread.interrupt()

cancel a Future by

Future.cancel(boolean mayInterruptIfRunning)

shutdown a Executor by

ExecutorService.shutdown() or ExecutorService.shutdownNow()

but, none of them give me an option to send a reason (information why interruption was performed). In other words I cannot throw and catch InterruptedException with more detailed message or cause.

EDIT: Based on Glains solution I've prepared a similar one but with using a 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();
    }
}

Upvotes: 1

Views: 533

Answers (1)

Glains
Glains

Reputation: 2873

You would have to write a custom API for that, as the Thread cannot be aborted with a specific reason. To make a Runnable able to be interrupted, you could create a custom interface that offers this functionality:

public interface Interruptable {

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

    void interrupt(Reason reason);
}

The defined reason can be anything, but the following may fit your use case:

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;
    }
}

Now, you would have to tweak your Runnable implementation so it can be interrupted. Please note that the following solution interrupts the thread gracefully, which means that the current iteration will finish, but a new one will not be started. If you want to truely interrupt the thread, you would need to store a reference to the thread executing the Runnable instances run method, since the interrupt method will not be called from the same thread. Then you can call Thread.interrupt on that reference.

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
        }
    }
}

Upvotes: 1

Related Questions