Есть ли способ убить дочерний поток после определенного срока в Java? Edit: Также этот конкретный поток может быть заблокирован в худшем случае (Thread используется для ожидания изменения файла и блокировки до тех пор, пока это событие не произойдет), поэтому я не уверен, что прерыв() будет успешным?
Убийство потока после определенного срока в Java
Ответ 1
Используйте ExecutorService
для выполнения Callable
, Callable
методы, в которых вы можете указать время ожидания. Например
ExecutorService executor = Executors.newSingleThreadExecutor();
executor.invokeAll(Arrays.asList(new Task()), 10, TimeUnit.MINUTES); // Timeout of 10 minutes.
executor.shutdown();
Здесь Task
конечно реализует Callable
.
Ответ 2
Почему бы не interrupt()
после определенного времени? Ваш порожденный поток должен иметь возможность правильно обрабатывать InterruptedException
.
См. эту статью (http://www.javaspecialists.eu/archive/Issue056.html) для получения дополнительной информации о отключении потоков.
См. также структуру Executor/Future, которые предоставляют полезные методы для сбора результатов и/или завершения потоков в определенные временные рамки.
Ответ 3
Не напрямую; Я думаю, что самый простой способ состоит в том, чтобы присоединиться() к этому потоку с этим ограничением по времени и прервать поток, если это не было сделано к моменту окончания соединения.
Так,
Thread t = ...
t.join(timelimit);
if (t.isAlive()) t.interrupt();
Заметьте, я использовал прерывание вместо того, чтобы его убить, это намного безопаснее. Я также рекомендовал бы использовать исполнителей вместо непосредственного управления потоками.
Ответ 4
Некоторые полезные изменения были внесены как часть 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));
}
}
Конечно, код выше легко может быть переписан как статический метод factory:
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);
}
Ответ 5
Вы можете использовать AOP и @Timeable
аннотацию для вашего метода из jcabi-aspects (я разработчик):
@Timeable(limit = 1, unit = TimeUnit.SECONDS)
String load(String resource) {
// do something time consuming
}
Когда достигнут предел времени, ваш поток получит флаг interrupted()
, установленный на true
, и ваше задание правильно справится с этой ситуацией и прекратит выполнение. Обычно это делается с помощью Thread.sleep(..)
.
Ответ 6
Убийство потока, как правило, является плохой идеей по причинам, связанным с документами API для Thread
.
Если вы умерли от убийства, используйте весь новый процесс.
В противном случае обычным делом будет опрос System.nanoTime
, опрос a (возможный volatile
) флаг, очередь "ядовитая таблетка" или что-то в этом роде.
Ответ 7
Брайан прав, прерывая его, безопаснее, чем "остановить" поток.
Что делать, если поток блокирует объект в середине модификации и внезапно останавливается (что приводит к блокировке блокировки)? Вы получаете странные результаты.
Ответ 8
Не используйте destroy()
, так как это не выполняет никакой очистки.
Самый простой способ - использовать join()
, например
try {
thread.join();
} catch (InterruptedException e) {//log exception...}
Вы можете использовать ExecutorService
. Это будет иметь большой смысл, если у вас есть несколько потоков, работающих одновременно. Если у вас есть необходимость создавать новые потоки, пока выполняются другие потоки, вы можете объединить это с BlockingQueue
.
A ThreadPoolExecutor
(an ExecutorService
-implementation) может принимать аргумент 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...}