开发者

Killing thread after some specified time limit in Java

开发者 https://www.devze.com 2022-12-28 18:13 出处:网络
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 bl

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?


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.


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


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.


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.


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(..).


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.


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.


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

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号