Как остановить поток

255

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

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

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

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

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

java_geek
источник
РЕДАКТИРОВАТЬ: Добавление щедрости, как мне нужно более четкий ответ. приведенный ниже код ExecutorService не решает мою проблему. Почему я должен спать () после выполнения моего кода? Если код завершен и sleep () прерван, как это может быть timeOut?
java_geek
7
Это sleep()была просто заглушка, чтобы представлять «долгосрочное задание». Просто замените его своим реальным заданием;)
BalusC
1
... "долго выполняющаяся задача", которая отвечает на interrupt()вызовы в ее потоке ... не все "блокирующие" вызовы делают, как я пытался указать в своем ответе. Специфика задачи, которую вы пытаетесь прервать, имеет огромное значение в подходе, который следует использовать. Дополнительная информация о задаче будет полезна.
erickson
Если эти ответы не решают проблему, то я думаю, что более подробная информация / код должны помочь ответить.
Elister
Эти темы, которые вы хотите ограничить по времени; они делают блокирующие вызовы, или они находятся в каком-то цикле, где вы можете легко проверить некоторую переменную, чтобы увидеть, пора ли выйти?
Скотт Смит

Ответы:

376

На самом деле лучше использовать 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.
}
BalusC
источник
24
Замените Thread.sleep(4000)другим длительным оператором, и пример не сработает. Другими словами, этот пример будет работать, только если Taskон предназначен для понимания Thread.isInterrupted()изменения статуса.
yegor256
@BalusC Я попробовал этот подход, пытаясь прекратить мои потоки, но не смог заставить его работать. Вы можете проверить это здесь: stackoverflow.com/questions/35553420/…
syfantid
Как обрабатывается InterruptedException при помощи future.cancel (true)?
Болей
1
n человек прокомментировал название пакета, и вот еще один +1 для него. Это такой хороший навык, чтобы впитывать. Спасибо!
Эшвин Тумма
@BalusC У меня есть сомнения, будет ли Future выполняться синхронно, и если это займет больше заранее определенного времени, то оно будет прервано. Иначе это произойдет в будущем, в то время как мы рассчитываем на время ... Спасибо
Адель Ахмад
49

Не существует 100% надежного способа сделать это для любой старой задачи. Задача должна быть написана с учетом этой способности.

Базовые библиотеки Java, такие как ExecutorServiceотмена асинхронных задач interrupt()вызовами в рабочем потоке. Так, например, если задача содержит какой-то цикл, вы должны проверять ее состояние прерывания на каждой итерации. Если задача выполняет операции ввода-вывода, они также должны быть прерываемыми, и их настройка может быть сложной. В любом случае имейте в виду, что код должен активно проверять наличие прерываний; установка прерывания не обязательно делает что-либо.

Конечно, если ваша задача представляет собой какой-то простой цикл, вы можете просто проверять текущее время на каждой итерации и сдаваться по истечении указанного времени ожидания. В этом случае рабочий поток не нужен.

Эриксон
источник
По моему опыту, единственный код, не реагирующий на прерванное начало, - это блокировка в нативном коде (ожидание операционной системы).
Турбьёрн Равн Андерсен
@ ThorbjørnRavnAndersen Я согласен, но это много кода. Я хочу сказать, что для этого нет универсального механизма; Вы должны понимать политику прерывания задачи.
Эриксон
@erickson, я согласен с твоим. Если говорить точнее, то для каждой задачи должна быть определена политика отмены, если вы заинтересованы в ее остановке. Или поток должен знать о том, что он должен делать, когда он прерывается. В конце концов, прерывание и остановка любого потока - это просто запрос, который целевой поток может принять или отклонить, поэтому лучше написать задачу, помня об этом.
AKS
не может executorservice выбрать запустить задачу в вызывающем потоке? Кроме того, executorservice может решить выполнить задачу в будущем?
filthy_wizard
@ user1232726 execute()Метод родительского интерфейса, Executorможет запустить задачу в вызывающем потоке. Нет аналогичного утверждения для submit()методов ExecutorServiceэтого возвращаемого Futureэкземпляра. Смысл службы заключается в том, что существуют рабочие потоки, которые необходимо очистить с помощью выключения, и что задачи выполняются асинхронно. Тем не менее, в контракте нет ничего, что говорило бы о том, что ExecutorServiceзапрещено выполнять задачи в потоке отправки; эти гарантии исходят от API-интерфейсов реализации, таких как Executorsфабрики.
Эриксон
13

Рассмотрите возможность использования экземпляра ExecutorService . Оба invokeAll()и invokeAny()методы доступны с timeoutпараметром.

Текущий поток будет блокироваться до тех пор, пока метод не завершится (не уверен, если это желательно), либо потому, что задача (и) выполнена нормально, либо истекло время ожидания. Вы можете проверить возвращенные Future(ие), чтобы определить, что произошло.

Дрю Уиллс
источник
9

Предполагая, что код потока находится вне вашего контроля:

Из документации Java, упомянутой выше:

Что если поток не отвечает на Thread.interrupt?

В некоторых случаях вы можете использовать специальные приемы приложения. Например, если поток ожидает на известном сокете, вы можете закрыть сокет, чтобы поток немедленно вернулся. К сожалению, на самом деле нет какой-либо техники, которая работает в целом. Следует отметить, что во всех ситуациях, когда ожидающий поток не отвечает на Thread.interrupt, он также не отвечает на Thread.stop. Такие случаи включают преднамеренные атаки типа «отказ в обслуживании» и операции ввода-вывода, для которых thread.stop и thread.interrupt не работают должным образом.

Нижняя граница:

Убедитесь, что все потоки могут быть прерваны, или же вам нужны специальные знания о потоке - например, наличие установленного флага. Может быть, вы можете потребовать, чтобы вам дали задачу вместе с кодом, необходимым для ее остановки - определите интерфейс с stop()методом. Вы также можете предупредить, когда вам не удалось остановить задачу.

Питер Ценг
источник
8

BalusC сказал:

Обновление: чтобы прояснить концептуальное недоразумение, сон () не требуется. Он просто используется для демонстрации SSCCE. Просто сделайте свое долгое бегущее задание прямо на месте сна ().

Но если вы замените Thread.sleep(4000);на, for (int i = 0; i < 5E8; i++) {}то он не скомпилируется, потому что пустой цикл не генерирует InterruptedException.

И для того, чтобы поток был прерываемым, он должен бросить InterruptedException.

Это кажется серьезной проблемой для меня. Я не понимаю, как адаптировать этот ответ для работы с общей долгосрочной задачей.

Отредактировано, чтобы добавить: я переосмыслил это как новый вопрос: [ прерывание потока через фиксированное время, должно ли оно генерировать InterruptedException? ]

user1310503
источник
Я делаю это, добавляя «исключение бросков» в общедоступный метод класса <T> call {}
Роберто Линарес,
5

Я думаю, что вы должны взглянуть на правильные механизмы обработки параллелизма (потоки, работающие в бесконечные циклы, сами по себе не кажутся хорошими, кстати). Убедитесь, что вы прочитали немного о теме «убивать» или «остановить» темы .

То, что вы описываете, звучит очень похоже на «рандеву», так что вы можете взглянуть на CyclicBarrier .

Могут быть и другие конструкции (например, использующие CountDownLatch ), которые могут решить вашу проблему (один поток ожидает с тайм-аутом для защелки, другой должен отсчитывать защелку, если он выполнил свою работу, которая освободит ваш первый поток либо после тайм-аут или когда вызывается отсчет защелки).

Я обычно рекомендую две книги в этой области: параллельное программирование на Java и параллелизм на практике .

Dieter
источник
5

Я создал вспомогательный класс только для этого некоторое время назад. Прекрасно работает:

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
 * TimeOut class - used for stopping a thread that is taking too long
 * @author Peter Goransson
 *
 */
public class TimeOut {

    Thread interrupter;
    Thread target;
    long timeout;
    boolean success;
    boolean forceStop;

    CyclicBarrier barrier;

    /**
     * 
     * @param target The Runnable target to be executed
     * @param timeout The time in milliseconds before target will be interrupted or stopped
     * @param forceStop If true, will Thread.stop() this target instead of just interrupt() 
     */
    public TimeOut(Runnable target, long timeout, boolean forceStop) {      
        this.timeout = timeout;
        this.forceStop = forceStop;

        this.target = new Thread(target);       
        this.interrupter = new Thread(new Interrupter());

        barrier = new CyclicBarrier(2); // There will always be just 2 threads waiting on this barrier
    }

    public boolean execute() throws InterruptedException {  

        // Start target and interrupter
        target.start();
        interrupter.start();

        // Wait for target to finish or be interrupted by interrupter
        target.join();  

        interrupter.interrupt(); // stop the interrupter    
        try {
            barrier.await(); // Need to wait on this barrier to make sure status is set
        } catch (BrokenBarrierException e) {
            // Something horrible happened, assume we failed
            success = false;
        } 

        return success; // status is set in the Interrupter inner class
    }

    private class Interrupter implements Runnable {

        Interrupter() {}

        public void run() {
            try {
                Thread.sleep(timeout); // Wait for timeout period and then kill this target
                if (forceStop) {
                  target.stop(); // Need to use stop instead of interrupt since we're trying to kill this thread
                }
                else {
                    target.interrupt(); // Gracefully interrupt the waiting thread
                }
                System.out.println("done");             
                success = false;
            } catch (InterruptedException e) {
                success = true;
            }


            try {
                barrier.await(); // Need to wait on this barrier
            } catch (InterruptedException e) {
                // If the Child and Interrupter finish at the exact same millisecond we'll get here
                // In this weird case assume it failed
                success = false;                
            } 
            catch (BrokenBarrierException e) {
                // Something horrible happened, assume we failed
                success = false;
            }

        }

    }
}

Это называется так:

long timeout = 10000; // number of milliseconds before timeout
TimeOut t = new TimeOut(new PhotoProcessor(filePath, params), timeout, true);
try {                       
  boolean sucess = t.execute(); // Will return false if this times out
  if (!sucess) {
    // This thread timed out
  }
  else {
    // This thread ran completely and did not timeout
  }
} catch (InterruptedException e) {}  
Питер Горанссон
источник
3

Я отправляю вам фрагмент кода, который показывает способ решения проблемы. В качестве примера я читаю файл. Вы можете использовать этот метод для другой операции, но вам нужно реализовать метод kill (), чтобы основная операция была прервана.

Надеюсь, поможет


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * Main class
 * 
 * @author el
 * 
 */
public class Main {
    /**
     * Thread which perform the task which should be timed out.
     * 
     * @author el
     * 
     */
    public static class MainThread extends Thread {
        /**
         * For example reading a file. File to read.
         */
        final private File fileToRead;
        /**
         * InputStream from the file.
         */
        final private InputStream myInputStream;
        /**
         * Thread for timeout.
         */
        final private TimeOutThread timeOutThread;

        /**
         * true if the thread has not ended.
         */
        boolean isRunning = true;

        /**
         * true if all tasks where done.
         */
        boolean everythingDone = false;

        /**
         * if every thing could not be done, an {@link Exception} may have
         * Happens.
         */
        Throwable endedWithException = null;

        /**
         * Constructor.
         * 
         * @param file
         * @throws FileNotFoundException
         */
        MainThread(File file) throws FileNotFoundException {
            setDaemon(false);
            fileToRead = file;
            // open the file stream.
            myInputStream = new FileInputStream(fileToRead);
            // Instantiate the timeout thread.
            timeOutThread = new TimeOutThread(10000, this);
        }

        /**
         * Used by the {@link TimeOutThread}.
         */
        public void kill() {
            if (isRunning) {
                isRunning = false;
                if (myInputStream != null) {
                    try {
                        // close the stream, it may be the problem.
                        myInputStream.close();
                    } catch (IOException e) {
                        // Not interesting
                        System.out.println(e.toString());
                    }
                }
                synchronized (this) {
                    notify();
                }
            }
        }

        /**
         * The task which should be timed out.
         */
        @Override
        public void run() {
            timeOutThread.start();
            int bytes = 0;
            try {
                // do something
                while (myInputStream.read() >= 0) {
                    // may block the thread.
                    myInputStream.read();
                    bytes++;
                    // simulate a slow stream.
                    synchronized (this) {
                        wait(10);
                    }
                }
                everythingDone = true;
            } catch (IOException e) {
                endedWithException = e;
            } catch (InterruptedException e) {
                endedWithException = e;
            } finally {
                timeOutThread.kill();
                System.out.println("-->read " + bytes + " bytes.");
                isRunning = false;
                synchronized (this) {
                    notifyAll();
                }
            }
        }
    }

    /**
     * Timeout Thread. Kill the main task if necessary.
     * 
     * @author el
     * 
     */
    public static class TimeOutThread extends Thread {
        final long timeout;
        final MainThread controlledObj;

        TimeOutThread(long timeout, MainThread controlledObj) {
            setDaemon(true);
            this.timeout = timeout;
            this.controlledObj = controlledObj;
        }

        boolean isRunning = true;

        /**
         * If we done need the {@link TimeOutThread} thread, we may kill it.
         */
        public void kill() {
            isRunning = false;
            synchronized (this) {
                notify();
            }
        }

        /**
         * 
         */
        @Override
        public void run() {
            long deltaT = 0l;
            try {
                long start = System.currentTimeMillis();
                while (isRunning && deltaT < timeout) {
                    synchronized (this) {
                        wait(Math.max(100, timeout - deltaT));
                    }
                    deltaT = System.currentTimeMillis() - start;
                }
            } catch (InterruptedException e) {
                // If the thread is interrupted,
                // you may not want to kill the main thread,
                // but probably yes.
            } finally {
                isRunning = false;
            }
            controlledObj.kill();
        }
    }

    /**
     * Start the main task and wait for the end.
     * 
     * @param args
     * @throws FileNotFoundException
     */
    public static void main(String[] args) throws FileNotFoundException {
        long start = System.currentTimeMillis();
        MainThread main = new MainThread(new File(args[0]));
        main.start();
        try {
            while (main.isRunning) {
                synchronized (main) {
                    main.wait(1000);
                }
            }
            long stop = System.currentTimeMillis();

            if (main.everythingDone)
                System.out.println("all done in " + (stop - start) + " ms.");
            else {
                System.out.println("could not do everything in "
                        + (stop - start) + " ms.");
                if (main.endedWithException != null)
                    main.endedWithException.printStackTrace();
            }
        } catch (InterruptedException e) {
            System.out.println("You've killed me!");
        }
    }
}

С уважением

ЭЛОУ
источник
3

Вот мой действительно простой в использовании вспомогательный класс для запуска или вызова части кода Java :-)

Это основано на превосходном ответе от BalusC

package com.mycompany.util.concurrent;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
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;

/**
 * Calling {@link Callable#call()} or Running {@link Runnable#run()} code
 * with a timeout based on {@link Future#get(long, TimeUnit))}
 * @author pascaldalfarra
 *
 */
public class CallableHelper
{

    private CallableHelper()
    {
    }

    public static final void run(final Runnable runnable, int timeoutInSeconds)
    {
        run(runnable, null, timeoutInSeconds);
    }

    public static final void run(final Runnable runnable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        call(new Callable<Void>()
        {
            @Override
            public Void call() throws Exception
            {
                runnable.run();
                return null;
            }
        }, timeoutCallback, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, int timeoutInSeconds)
    {
        return call(callable, null, timeoutInSeconds); 
    }

    public static final <T> T call(final Callable<T> callable, Runnable timeoutCallback, int timeoutInSeconds)
    {
        ExecutorService executor = Executors.newSingleThreadExecutor();
        try
        {
            Future<T> future = executor.submit(callable);
            T result = future.get(timeoutInSeconds, TimeUnit.SECONDS);
            System.out.println("CallableHelper - Finished!");
            return result;
        }
        catch (TimeoutException e)
        {
            System.out.println("CallableHelper - TimeoutException!");
            if(timeoutCallback != null)
            {
                timeoutCallback.run();
            }
        }
        catch (InterruptedException e)
        {
            e.printStackTrace();
        }
        catch (ExecutionException e)
        {
            e.printStackTrace();
        }
        finally
        {
            executor.shutdownNow();
            executor = null;
        }

        return null;
    }

}
паскаль
источник
2

Следующий фрагмент запускает операцию в отдельном потоке, затем ждет до 10 секунд, пока операция не завершится. Если операция не завершится вовремя, код попытается отменить операцию, а затем продолжить свой веселый путь. Даже если операция не может быть легко отменена, родительский поток не будет ожидать завершения дочернего потока.

ExecutorService executorService = getExecutorService();
Future<SomeClass> future = executorService.submit(new Callable<SomeClass>() {
    public SomeClass call() {
        // Perform long-running task, return result. The code should check
        // interrupt status regularly, to facilitate cancellation.
    }
});
try {
    // Real life code should define the timeout as a constant or
    // retrieve it from configuration
    SomeClass result = future.get(10, TimeUnit.SECONDS);
    // Do something with the result
} catch (TimeoutException e) {
    future.cancel(true);
    // Perform other error handling, e.g. logging, throwing an exception
}

getExecutorService()Метод может быть реализован несколькими способами. Если у вас нет особых требований, вы можете просто вызвать Executors.newCachedThreadPool()пул потоков без верхнего предела количества потоков.

markusk
источник
Какой импорт требуется? Какие есть SomeClassи Future?
ADTC
2

Одна вещь, о которой я не упомянул, это то, что убийство потоков - это, как правило, плохая идея. Существуют методы для того, чтобы сделать потоковые методы полностью прерываемыми , но это отличается от простого уничтожения потока после истечения времени ожидания.

Риск того, что вы предлагаете, заключается в том, что вы, вероятно, не знаете, в каком состоянии будет поток, когда вы его убьете, поэтому вы рискуете ввести нестабильность. Лучшее решение состоит в том, чтобы убедиться, что ваш многопоточный код либо не зависает, либо будет хорошо отвечать на запрос прерывания.

Дэн Пьюзи
источник
Без контекста утверждение, подобное вашему, звучит слишком ограничительно. В академической обстановке мне очень часто приходится проверять что-либо до истечения времени ожидания, и когда это происходит, я просто отбрасываю все вычисления и записываю, что истекло время ожидания. Вероятно, это редкость в отрасли, но все же ...
Алессандро С.
@AlessandroS: это разумный момент, хотя ОП попросил «лучшие решения», под которыми я понимал, что надежность и надежность предпочтительнее грубой силы.
Дэн Пьюзи
2

Отличный ответ от BalusC:

но просто добавлю, что само время ожидания не прерывает сам поток. даже если вы проверяете с помощью while (! Thread.interrupted ()) в своей задаче. если вы хотите убедиться, что поток остановлен, вы также должны убедиться в том, что future.cancel () вызывается при перехвате исключения тайм-аута.

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) {
            //Without the below cancel the thread will continue to live 
            // even though the timeout exception thrown.
            future.cancel();
            System.out.println("Terminated!");
        } 

        executor.shutdownNow();
    } 
} 

class Task implements Callable<String> {
    @Override 
    public String call() throws Exception {
      while(!Thread.currentThread.isInterrupted()){
          System.out.println("Im still running baby!!");
      }          
    } 
} 
Robocide
источник
0

Я думаю, что ответ в основном зависит от самой задачи.

  • Это делает одну задачу снова и снова?
  • Необходимо ли, чтобы таймаут прерывал текущую задачу сразу же после ее истечения?

Если первый ответ - да, а второй - нет, вы можете сделать это так просто:

public class Main {

    private static final class TimeoutTask extends Thread {
        private final long _timeoutMs;
        private Runnable _runnable;

        private TimeoutTask(long timeoutMs, Runnable runnable) {
            _timeoutMs = timeoutMs;
            _runnable = runnable;
        }

        @Override
        public void run() {
            long start = System.currentTimeMillis();
            while (System.currentTimeMillis() < (start + _timeoutMs)) {
                _runnable.run();
            }
            System.out.println("execution took " + (System.currentTimeMillis() - start) +" ms");
        }

    }

    public static void main(String[] args) throws Exception {
        new TimeoutTask(2000L, new Runnable() {

            @Override
            public void run() {
                System.out.println("doing something ...");
                try {
                    // pretend it's taking somewhat longer than it really does
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }).start();
    }
}

Если это не вариант, сузьте ваши требования или покажите какой-нибудь код.

sfussenegger
источник
0

Я искал ExecutorService, который может прервать все тайм-ауты Runnable, выполненные им, но не нашел ни одного. Через несколько часов я создал один, как показано ниже. Этот класс может быть изменен для повышения надежности.

public class TimedExecutorService extends ThreadPoolExecutor {
    long timeout;
    public TimedExecutorService(int numThreads, long timeout, TimeUnit unit) {
        super(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(numThreads + 1));
        this.timeout = unit.toMillis(timeout);
    }

    @Override
    protected void beforeExecute(Thread thread, Runnable runnable) {
        Thread interruptionThread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    // Wait until timeout and interrupt this thread
                    Thread.sleep(timeout);
                    System.out.println("The runnable times out.");
                    thread.interrupt();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        interruptionThread.start();
    }
}

Использование:

public static void main(String[] args) {

    Runnable abcdRunnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("abcdRunnable started");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // logger.info("The runnable times out.");
            }
            System.out.println("abcdRunnable ended");
        }
    };

    Runnable xyzwRunnable = new Runnable() {
        @Override
        public void run() {
            System.out.println("xyzwRunnable started");
            try {
                Thread.sleep(20000);
            } catch (InterruptedException e) {
                // logger.info("The runnable times out.");
            }
            System.out.println("xyzwRunnable ended");
        }
    };

    int numThreads = 2, timeout = 5;
    ExecutorService timedExecutor = new TimedExecutorService(numThreads, timeout, TimeUnit.SECONDS);
    timedExecutor.execute(abcdRunnable);
    timedExecutor.execute(xyzwRunnable);
    timedExecutor.shutdown();
}
Том
источник
0

Теперь я сталкиваюсь с такой проблемой. Это происходит, чтобы декодировать изображение. Процесс декодирования занимает слишком много времени, чтобы экран оставался черным. l добавить контроллер времени: если время слишком велико, то всплывающее окно из текущего потока. Ниже приводится разница:

   ExecutorService executor = Executors.newSingleThreadExecutor();
   Future<Bitmap> future = executor.submit(new Callable<Bitmap>() {
       @Override
       public Bitmap call() throws Exception {
       Bitmap bitmap = decodeAndScaleBitmapFromStream(context, inputUri);// do some time consuming operation
       return null;
            }
       });
       try {
           Bitmap result = future.get(1, TimeUnit.SECONDS);
       } catch (TimeoutException e){
           future.cancel(true);
       }
       executor.shutdown();
       return (bitmap!= null);
Лю Цзин
источник
0

У меня такая же проблема. Поэтому я пришел к такому простому решению.

public class TimeoutBlock {

 private final long timeoutMilliSeconds;
    private long timeoutInteval=100;

    public TimeoutBlock(long timeoutMilliSeconds){
        this.timeoutMilliSeconds=timeoutMilliSeconds;
    }

    public void addBlock(Runnable runnable) throws Throwable{
        long collectIntervals=0;
        Thread timeoutWorker=new Thread(runnable);
        timeoutWorker.start();
        do{ 
            if(collectIntervals>=this.timeoutMilliSeconds){
                timeoutWorker.stop();
                throw new Exception("<<<<<<<<<<****>>>>>>>>>>> Timeout Block Execution Time Exceeded In "+timeoutMilliSeconds+" Milli Seconds. Thread Block Terminated.");
            }
            collectIntervals+=timeoutInteval;           
            Thread.sleep(timeoutInteval);

        }while(timeoutWorker.isAlive());
        System.out.println("<<<<<<<<<<####>>>>>>>>>>> Timeout Block Executed Within "+collectIntervals+" Milli Seconds.");
    }

    /**
     * @return the timeoutInteval
     */
    public long getTimeoutInteval() {
        return timeoutInteval;
    }

    /**
     * @param timeoutInteval the timeoutInteval to set
     */
    public void setTimeoutInteval(long timeoutInteval) {
        this.timeoutInteval = timeoutInteval;
    }
}

Гарантирует, что если блок не будет выполнен в течение срока. процесс завершится и выдаст исключение.

пример :

try {
        TimeoutBlock timeoutBlock = new TimeoutBlock(10 * 60 * 1000);//set timeout in milliseconds
        Runnable block=new Runnable() {

            @Override
            public void run() {
                //TO DO write block of code 
            }
        };

        timeoutBlock.addBlock(block);// execute the runnable block 

    } catch (Throwable e) {
        //catch the exception here . Which is block didn't execute within the time limit
    }
Нирошан Абейвикрама
источник
0

В решении, предоставленном BalusC , основной поток остается заблокированным в течение периода ожидания. Если у вас есть пул потоков с более чем одним потоком, вам потребуется то же количество дополнительных потоков, которые будут использовать блокирующий вызов Future.get (long timeout, TimeUnit unit) для ожидания и закрытия потока, если он превышает период времени ожидания.

Общее решение этой проблемы - создать декоратор ThreadPoolExecutor, который может добавить функцию тайм-аута. Этот класс Decorator должен создать столько потоков, сколько имеется в ThreadPoolExecutor, и все эти потоки следует использовать только для ожидания и закрытия ThreadPoolExecutor.

Универсальный класс должен быть реализован как показано ниже:

import java.util.List;
import java.util.concurrent.*;

public class TimeoutThreadPoolDecorator extends ThreadPoolExecutor {


    private final ThreadPoolExecutor commandThreadpool;
    private final long timeout;
    private final TimeUnit unit;

    public TimeoutThreadPoolDecorator(ThreadPoolExecutor threadpool,
                                      long timeout,
                                      TimeUnit unit ){
        super(  threadpool.getCorePoolSize(),
                threadpool.getMaximumPoolSize(),
                threadpool.getKeepAliveTime(TimeUnit.MILLISECONDS),
                TimeUnit.MILLISECONDS,
                threadpool.getQueue());

        this.commandThreadpool = threadpool;
        this.timeout=timeout;
        this.unit=unit;
    }

    @Override
    public void execute(Runnable command) {
        super.execute(() -> {
            Future<?> future = commandThreadpool.submit(command);
            try {
                future.get(timeout, unit);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } catch (ExecutionException | TimeoutException e) {
                throw new RejectedExecutionException(e);
            } finally {
                future.cancel(true);
            }
        });
    }

    @Override
    public void setCorePoolSize(int corePoolSize) {
        super.setCorePoolSize(corePoolSize);
        commandThreadpool.setCorePoolSize(corePoolSize);
    }

    @Override
    public void setThreadFactory(ThreadFactory threadFactory) {
        super.setThreadFactory(threadFactory);
        commandThreadpool.setThreadFactory(threadFactory);
    }

    @Override
    public void setMaximumPoolSize(int maximumPoolSize) {
        super.setMaximumPoolSize(maximumPoolSize);
        commandThreadpool.setMaximumPoolSize(maximumPoolSize);
    }

    @Override
    public void setKeepAliveTime(long time, TimeUnit unit) {
        super.setKeepAliveTime(time, unit);
        commandThreadpool.setKeepAliveTime(time, unit);
    }

    @Override
    public void setRejectedExecutionHandler(RejectedExecutionHandler handler) {
        super.setRejectedExecutionHandler(handler);
        commandThreadpool.setRejectedExecutionHandler(handler);
    }

    @Override
    public List<Runnable> shutdownNow() {
        List<Runnable> taskList = super.shutdownNow();
        taskList.addAll(commandThreadpool.shutdownNow());
        return taskList;
    }

    @Override
    public void shutdown() {
        super.shutdown();
        commandThreadpool.shutdown();
    }
}

Вышеуказанный декоратор может быть использован как ниже:

import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {

    public static void main(String[] args){

        long timeout = 2000;

        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(3, 10, 0, TimeUnit.MILLISECONDS, new SynchronousQueue<>(true));

        threadPool = new TimeoutThreadPoolDecorator( threadPool ,
                timeout,
                TimeUnit.MILLISECONDS);


        threadPool.execute(command(1000));
        threadPool.execute(command(1500));
        threadPool.execute(command(2100));
        threadPool.execute(command(2001));

        while(threadPool.getActiveCount()>0);
        threadPool.shutdown();


    }

    private static Runnable command(int i) {

        return () -> {
            System.out.println("Running Thread:"+Thread.currentThread().getName());
            System.out.println("Starting command with sleep:"+i);
            try {
                Thread.sleep(i);
            } catch (InterruptedException e) {
                System.out.println("Thread "+Thread.currentThread().getName()+" with sleep of "+i+" is Interrupted!!!");
                return;
            }
            System.out.println("Completing Thread "+Thread.currentThread().getName()+" after sleep of "+i);
        };

    }
}
Сумит Саху
источник