Уничтожение потока после определенного срока в Java
Есть ли способ убить дочерний поток после определенного срока в Java? Изменить: Также этот конкретный поток может быть заблокирован в худшем случае (поток используется для ожидания изменения файла и блокирует, пока это событие не произойдет), поэтому я не уверен, что interrupt() будет успешным?
8 ответов
Использовать ExecutorService
выполнить Callable
, проверьте методы, в которых вы можете указать время ожидания. Например
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.invokeAll(Arrays.asList(new Task()), 10, TimeUnit.MINUTES); // Timeout of 10 minutes.
executor.shutdown();
Вот Task
конечно реализует Callable
,
Некоторые полезные изменения были внесены как часть JEP 266 в CompletableFuture
начиная с Java 9. Используя метод orTimeout, на данный момент его можно написать так:
CompletableFuture.runAsync(thread::run)
.orTimeout(30, TimeUnit.SECONDS)
.exceptionally(throwable -> {
log.error("An error occurred", throwable);
return null;
});
В Java 8, к сожалению, вы должны использовать дополнительный код. Вот пример использования шаблона делегирования с помощью 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));
}
}
Конечно, приведенный выше код легко можно переписать как простой статический метод:
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);
}
Не напрямую; Я думаю, что самый простой способ состоит в том, чтобы присоединиться () к этому потоку с этим ограничением по времени и прервать поток, если это не будет сделано к моменту окончания соединения.
Так,
Thread t = ...
t.join(timelimit);
if (t.isAlive) t.interrupt();
Обратите внимание, что я использовал прерывание вместо того, чтобы его убивать, это намного безопаснее. Я также рекомендовал бы использовать исполнителей вместо непосредственного управления потоками.
Почему бы и нет interrupt()
это после определенного времени? Ваша порожденная нить должна быть в состоянии справиться с InterruptedException
должным образом.
См. Эту статью ( http://www.javaspecialists.eu/archive/Issue056.html) для получения дополнительной информации о чистом отключении потоков.
См. Также структуру Executor/Future, которая предоставляет полезные методы для сбора результатов и / или завершения потоков в определенных временных рамках.
Вы можете использовать АОП и @Timeable
аннотация для вашего метода из jcabi-аспектов (я разработчик):
@Timeable(limit = 1, unit = TimeUnit.SECONDS)
String load(String resource) {
// do something time consuming
}
Когда будет достигнут срок, ваша нить получит interrupted()
флаг установлен в true
и ваша задача - правильно справиться с этой ситуацией и остановить выполнение. Обычно это делается Thread.sleep(..)
,
Брайан прав, прерывать его безопаснее, чем "останавливать" нить.
Что, если поток блокирует середину модификации объекта и внезапно останавливается (что приводит к снятию блокировки)? Вы получаете странные результаты.
Уничтожение потока, как правило, плохая идея по причинам, связанным с документами API для Thread
,
Если вы не готовы убивать, используйте совершенно новый процесс.
В противном случае обычно нужно провести опрос System.nanoTime
, опрос (возможно) volatile
) флаг, очередь "таблетка с ядом" или что-то в этом роде.
Не использовать destroy()
так как это не выполняет никакой очистки.
Самый простой способ заключается в использовании join()
, лайк
try {
thread.join();
} catch (InterruptedException e) {//log exception...}
Вы могли бы использовать ExecutorService
, Это имело бы большой смысл, если у вас есть несколько потоков, запущенных одновременно. Если вам нужно порождать новые потоки во время работы других потоков, вы можете комбинировать это с BlockingQueue
,
ThreadPoolExecutor
(ExecutorService
Реализация) может занять BlockingQueue
в качестве аргумента, и вы можете просто добавить новые темы в очередь. Когда вы закончите, вы просто прекратите ThreadPoolExecutor
,
private BlockingQueue<Runnable> queue;
...
ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, new Long(1000),
TimeUnit.MILLISECONDS, this.queue);
Вы можете вести подсчет всех потоков, добавленных в очередь. Когда вы думаете, что все готово (очередь пуста, возможно?), Просто сравните это с
if (issuedThreads == pool.getCompletedTaskCount()) {
pool.shutdown();
}
Если два совпадения, вы сделали. Другой способ завершить пул - это подождать секунду в цикле:
try {
while (!this.pool.awaitTermination(1000, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {//log exception...}