Вопрос-ответ

How to timeout a thread

Как установить тайм-аут для потока

Я хочу запускать поток в течение некоторого фиксированного промежутка времени. Если он не будет завершен в течение этого времени, я хочу либо завершить его, сгенерировать какое-либо исключение, либо обработать его каким-либо образом. Как это можно сделать?

Один из способов сделать это, как я понял из этого потока, - использовать TimerTask внутри метода run() потока.

Есть ли какие-нибудь лучшие решения для этого?

 
РЕДАКТИРОВАТЬ: Добавляю вознаграждение, поскольку мне нужен был более четкий ответ. Приведенный ниже код ExecutorService не решает мою проблему. Почему я должен sleep() после выполнения (некоторого кода - у меня нет дескриптора для этого фрагмента кода)? Если код завершен, а sleep() прерван, как это может быть тайм-аутом?

Задача, которая должна быть выполнена, не в моей власти. Это может быть любой фрагмент кода. Проблема в том, что этот фрагмент кода может запустить бесконечный цикл. Я не хочу, чтобы это произошло. Итак, я просто хочу запустить эту задачу в отдельном потоке. Родительский поток должен дождаться завершения этого потока и должен знать состояние задачи (т. е. Истекло ли время ожидания, произошло ли какое-либо исключение или оно прошло успешно). Если задача переходит в бесконечный цикл, мой родительский поток продолжает ждать бесконечно, что не является идеальной ситуацией.

Переведено автоматически
Ответ 1

Действительно, лучше использовать ExecutorService вместо Timer, вот SSCCE:

package com.stackoverflow.q2275443;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class Test {
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new Task());

try {
System.out.println("Started..");
System.out.println(future.get(3, TimeUnit.SECONDS));
System.out.println("Finished!");
} catch (TimeoutException e) {
future.cancel(true);
System.out.println("Terminated!");
}

executor.shutdownNow();
}
}

class Task implements Callable<String> {
@Override
public String call() throws Exception {
Thread.sleep(4000); // Just to demo a long running task of 4 seconds.
return "Ready!";
}
}

Немного поиграйте с timeout аргументом в Future#get() методе, например, увеличьте его до 5, и вы увидите, что поток завершается. Вы можете перехватить тайм-аут в catch (TimeoutException e) блоке.

Обновление: чтобы прояснить концептуальное недоразумение, sleep() это не требуется. Это просто используется для SSCCE / демонстрационных целей. Просто выполните свою длительную задачу прямо там, вместо sleep(). Внутри вашей длительно выполняющейся задачи вы должны проверять, не прерван ли поток следующим образом:

while (!Thread.interrupted()) {
// Do your long running task here.
}
Ответ 2

There isn't a 100% reliable way to do this for any old task. The task has to be written with this ability in mind.

Core Java libraries like ExecutorService cancel asynchronous tasks with interrupt() calls on the worker thread. So, for example, if the task contains some sort of loop, you should be checking its interrupt status on each iteration. If the task is doing I/O operations, they should be interruptible too—and setting that up can be tricky. In any case, keep in mind that code has to actively check for interrupts; setting an interrupt doesn't necessarily do anything.

Of course, if your task is some simple loop, you can just check the current time at each iteration and give up when a specified timeout has elapsed. A worker thread isn't needed in that case.

Ответ 3

Consider using an instance of ExecutorService. Both invokeAll() and invokeAny() methods are available with a timeout parameter.

The current thread will block until the method completes (not sure if this is desirable) either because the task(s) completed normally or the timeout was reached. You can inspect the returned Future(s) to determine what happened.

Ответ 4

Assuming the thread code is out of your control:

From the Java documentation mentioned above:


What if a thread doesn't respond to Thread.interrupt?


In some cases, you can use application specific tricks. For example,
if a thread is waiting on a known socket, you can close the socket to
cause the thread to return immediately. Unfortunately, there really
isn't any technique that works in general. It should be noted that in
all situations where a waiting thread doesn't respond to
Thread.interrupt, it wouldn't respond to Thread.stop either.
Such
cases include deliberate denial-of-service attacks, and I/O operations
for which thread.stop and thread.interrupt do not work properly.


Bottom Line:

Make sure all threads can be interrupted, or else you need specific knowledge of the thread - like having a flag to set. Maybe you can require that the task be given to you along with the code needed to stop it - define an interface with a stop() method. You can also warn when you failed to stop a task.

java multithreading