Как остановить поток - PullRequest
       36

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

236 голосов
/ 16 февраля 2010

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

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

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


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

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

Ответы [ 17 ]

2 голосов
/ 01 марта 2010

Следующий фрагмент запускает операцию в отдельном потоке, затем ждет до 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() для пула потоков без верхнего предела количества потоков.

2 голосов
/ 28 февраля 2010

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

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

0 голосов
/ 24 октября 2018

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

Общим решением этой проблемы является создание ThreadPoolExecutor Decorator, который может добавить функцию тайм-аута. Этот класс 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);
        };

    }
}
0 голосов
/ 10 августа 2017

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

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 голосов
/ 20 ноября 2015

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

   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 голосов
/ 05 августа 2015

Я искал 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 голосов
/ 23 февраля 2010

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

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

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

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();
    }
}

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

...