38

Is there a way to kill a child thread after some specified time limit in Java? Edit: Also this particular thread may be blocked in its worst case (Thread is used to wait for a file modification and blocks until this event occurs), so im not sure that interrupt() will be successful?

Traker
  • 2,147
  • 3
  • 23
  • 38
  • What are you using to wait for the file modification? Object.wait() or something else? – mdma Apr 28 '10 at 22:45
  • 1
    Actually I am using jpathwatch to look for file modifications and specifically 'watchService.take()' which will block until a file has been created/deleted. – Traker Apr 29 '10 at 13:02

8 Answers8

45

Make use of ExecutorService to execute the Callable, checkout the methods wherein you can specify the timeout. E.g.

ExecutorService executor = Executors.newSingleThreadExecutor();
executor.invokeAll(Arrays.asList(new Task()), 10, TimeUnit.MINUTES); // Timeout of 10 minutes.
executor.shutdown();

Here Task of course implements Callable.

Stephan
  • 41,764
  • 65
  • 238
  • 329
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Good suggestions, but will the ExecutorService be able to timeout the thread even if it is blocked? – Traker Apr 28 '10 at 21:59
  • 1
    It will interrupt the thread, yes. If you like to hook on it, make use of `ExecutorService#awaitTermination()`, also see the example in the Javadoc's introduction. – BalusC Apr 28 '10 at 22:05
  • 25
    IMHO, `invokeAll` is overkill. Just use `executor.submit(new Task()).get(10, TimeUnit.MINUTES);` – Peter Lawrey Apr 27 '11 at 07:06
  • 6
    @BalusC: `ExecutorService` documentation indicates that `invokeAll` will block the current thread: "Executes the given tasks, returning a list of Futures holding their status and results when all complete or the timeout expires, whichever happens first." Also I think `Task` must implement `Callable` not `Runnable`. – Russell Silva Aug 02 '13 at 18:51
  • This solution could be acceptable for single thread executor only. Otherwise (if you have > 1 threads in executor service) this is not the case. – Volodymyr Kret Apr 29 '20 at 07:30
8

Some helpful changes were introduced as part of JEP 266 in CompletableFuture since Java 9. Using orTimeout method, for now, it is possible to write it like:

CompletableFuture.runAsync(thread::run)
    .orTimeout(30, TimeUnit.SECONDS)
    .exceptionally(throwable -> {
        log.error("An error occurred", throwable);
        return null;
    });

In Java 8, unfortunately, you should use some extra code. Here is an example of delegation pattern usage with help of Lombok:

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import java.time.Duration;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.util.concurrent.TimeoutException;
import static lombok.AccessLevel.PRIVATE;
import lombok.AllArgsConstructor;
import lombok.experimental.Delegate;

@AllArgsConstructor(access = PRIVATE)
public class TimeoutableCompletableFuture<T> extends CompletableFuture<T> {

    public static TimeoutableCompletableFuture<Void> runAsync(
            Runnable runnable) {
        return new TimeoutableCompletableFuture<>(
                CompletableFuture.runAsync(runnable));
    }

    @Delegate
    private final CompletableFuture<T> baseFuture;

    public TimeoutableCompletableFuture<T> orTimeout(Duration duration) {
        final CompletableFuture<T> otherFuture = new CompletableFuture<>();
        Executors.newScheduledThreadPool(
                1,
                new ThreadFactoryBuilder()
                .setDaemon(true)
                .setNameFormat("timeoutable-%d")
                .build())
                .schedule(() -> {
                    TimeoutException ex = new TimeoutException(
                            "Timeout after " + duration);
                    return otherFuture.completeExceptionally(ex);
                }, duration.toMillis(), MILLISECONDS);

        return new TimeoutableCompletableFuture<>(
                baseFuture.applyToEither(otherFuture, a -> a));
    }
}

Of course, the code above easily could be rewritten as just a static factory method:

public static CompletableFuture<Void> runAsyncOrTimeout(
        Runnable runnable, long timeout, TimeUnit unit) {

    CompletableFuture<Void> other = new CompletableFuture<>();
    Executors.newScheduledThreadPool(
            1,
            new ThreadFactoryBuilder()
            .setDaemon(true)
            .setNameFormat("timeoutafter-%d")
            .build())
            .schedule(() -> {
                TimeoutException ex = new TimeoutException(
                        "Timeout after " + timeout);
                return other.completeExceptionally(ex);
            }, timeout, unit);
    return CompletableFuture.runAsync(runnable).applyToEither(other, a -> a);
}
ytterrr
  • 3,036
  • 6
  • 23
  • 32
7

Not directly; I think the simplest way is to join() on that thread with that time limit, and interrupt the thread if it's not done by the time the join ended.

So,

Thread t = ...
t.join(timelimit);
if (t.isAlive()) t.interrupt();

Notice I used interrupt instead of actually killing it, it's much safer. I would also recommend using executors instead of directly manipulating threads.

Ankur
  • 50,282
  • 110
  • 242
  • 312
Oak
  • 26,231
  • 8
  • 93
  • 152
  • 3
    This won't work well if the other thread grabs all CPU, thus the join is only _started_ after a too large delay. – mafu Jul 05 '14 at 12:37
6

Why not interrupt() it after a particular time ? Your spawned thread will have to be able to handle an InterruptedException properly.

See this article (http://www.javaspecialists.eu/archive/Issue056.html) for more information on shutting down threads cleanly.

See also the Executor/Future framework, which provide useful methods for collecting results and/or terminating threads within particular time limits.

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • 1
    The thread only needs to respond to `InterruptedException` if it makes a call that can throw it. If it is spinning in a loop, it should be checking its own interrupted flag using `Thread.isInterrupted()` (or just `Thread.interrupted()`, implying the current thread). – Mike Daniels Apr 28 '10 at 21:56
3

You can use AOP and a @Timeable annotation for your method from jcabi-aspects (I'm a developer):

@Timeable(limit = 1, unit = TimeUnit.SECONDS)
String load(String resource) {
  // do something time consuming
}

When time limit is reached your thread will get interrupted() flag set to true and it's your job to handle this situation correctly and to stop execution. Normally it's done by Thread.sleep(..).

yegor256
  • 102,010
  • 123
  • 446
  • 597
1

Killing a thread is generally a bad idea for reasons linked to for the API docs for Thread.

If you are dead set on killing, use a whole new process.

Otherwise the usual thing is to have the thread poll System.nanoTime, poll a (possible volatile) flag, queue a "poison pill" or something of that nature.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
1

Brian's right, interrupting it is safer than "stopping" the thread.
What if the thread is locking on an object mid-modification, and suddenly gets stopped (which causes the lock to be released)? You get weird results.

Ryan P.
  • 855
  • 2
  • 14
  • 20
0

Do not use destroy() since that does not perform any cleanup.

The most straightforward way is to use join(), like

try {
     thread.join();
} catch (InterruptedException e) {//log exception...}

You could use an ExecutorService. That would make a lot of sense if you have several threads running concurrently. If you have the need to spawn new threads while other threads are running, you can combine this with a BlockingQueue.

A ThreadPoolExecutor (an ExecutorService-implementation) can take a BlockingQueue as argument, and you can simply add new threads to the queue. When you are done you simply terminate the ThreadPoolExecutor.

private BlockingQueue<Runnable> queue;
...
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, new Long(1000), 
                TimeUnit.MILLISECONDS, this.queue);

You can keep a count of all the threads added to the queue. When you think you are done (the queue is empty, perhaps?) simply compare this to

 if (issuedThreads == pool.getCompletedTaskCount()) {
        pool.shutdown();
    }

If the two match, you are done. Another way to terminate the pool is to wait a second in a loop:

try {
      while (!this.pool.awaitTermination(1000, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {//log exception...}
Lars Andren
  • 8,601
  • 7
  • 41
  • 56