Подтвердить что ты не робот

Убийство потока после определенного срока в Java

Есть ли способ убить дочерний поток после определенного срока в Java? Edit: Также этот конкретный поток может быть заблокирован в худшем случае (Thread используется для ожидания изменения файла и блокировки до тех пор, пока это событие не произойдет), поэтому я не уверен, что прерыв() будет успешным?

4b9b3361

Ответ 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...}