Killing thread after some specified time limit in

2019-01-02 15: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 blocks until this event occurs), so im not sure that interrupt() will be successful?

8条回答
浮光初槿花落
2楼-- · 2019-01-02 15:53

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

查看更多
还给你的自由
3楼-- · 2019-01-02 15:53

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);
}
查看更多
心情的温度
4楼-- · 2019-01-02 15:56

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.

查看更多
泛滥B
5楼-- · 2019-01-02 15:59

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.

查看更多
大哥的爱人
6楼-- · 2019-01-02 16:00

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...}
查看更多
步步皆殇っ
7楼-- · 2019-01-02 16:01

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.

查看更多
登录 后发表回答