Как отключить поток
Я хочу запустить поток в течение некоторого фиксированного количества времени. Если он не завершен в течение этого времени, я хочу либо убить его, либо выбросить какое-то исключение, либо обработать его каким-то образом. Как это можно сделать?
Один из способов сделать это, как я понял из этот поток
заключается в использовании TimerTask внутри метода run() Thread.
Есть ли лучшие решения для этого?
EDIT: добавление щедрости, поскольку мне нужен более четкий ответ. Код ExecutorService, приведенный ниже, не затрагивает мою проблему. Почему я должен спать() после выполнения (какой-то код - я не могу обработать этот кусок кода)? Если код завершен и функция sleep() прерывается, как это может быть timeOut?
Задача, которую нужно выполнить, не входит в мой контроль. Это может быть любой фрагмент кода. Проблема в том, что эта часть кода может работать в бесконечном цикле. Я не хочу, чтобы это произошло. Поэтому я просто хочу запустить эту задачу в отдельном потоке. Родительский поток должен дождаться завершения этого потока и должен знать статус задачи (т.е. Время ожидания или какое-то исключение или если это успех). Если задача переходит в бесконечный цикл, мой родительский поток продолжает ждать бесконечно, что не является идеальной ситуацией.
Ответы
Ответ 1
Действительно, используйте 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()
. В вашей долгосрочной задаче вы должны проверить, не является ли поток interrupted следующим образом:
while (!Thread.interrupted()) {
// Do your long running task here.
}
Ответ 2
Существует не 100% надежный способ сделать это для любой старой задачи. Задача должна быть написана с учетом этой способности.
Основные Java библиотеки ExecutorService
отменяют асинхронных задач с interrupt()
вызывает на рабочий поток. Так, например, если задача содержит какой-то цикл, вы должны проверить ее состояние
Ответ 3
Рассмотрим использование экземпляра ExecutorService. Оба метода invokeAll()
и invokeAny()
доступны с параметром timeout
.
Текущий поток будет блокироваться до тех пор, пока метод не завершится (не уверен, что это желательно) либо из-за того, что задачи завершены нормально, либо таймаут был достигнут. Вы можете проверить возвращенные Future
(s), чтобы определить, что произошло.
Ответ 4
BalusC сказал:
Обновление: чтобы прояснить концептуальное недоразумение, sleep() не требуется. Он используется только для целей SSCCE/демонстрации. Просто выполняйте свою длинную работу прямо там вместо sleep().
Но если вы замените Thread.sleep(4000);
на for (int i = 0; i < 5E8; i++) {}
, то он не скомпилируется, потому что пустой цикл не бросает InterruptedException
.
И для того, чтобы поток был прерываемым, ему нужно выбросить InterruptedException
.
Это кажется серьезной проблемой для меня. Я не могу понять, как адаптировать этот ответ для работы с общей долговременной задачей.
Отредактировано для добавления: я заново задал это как новый вопрос: [прерывание потока после установленного времени, нужно ли его перехватывать InterruptedException?]
Ответ 5
Предполагая, что код потока вышел из-под контроля:
Из документации Java упомянутой выше:
Что делать, если поток не отвечает Thread.interrupt?
В некоторых случаях вы можете использовать трюки, специфичные для приложения. Например, если нить ожидает в известном сокете, вы можете закрыть сокет чтобы поток немедленно возвращался. К сожалению, действительно не является какой-либо техникой, которая работает в целом. Следует отметить, что в все ситуации, когда ожидающий поток не отвечает на Thread.interrupt, он не будет отвечать на Thread.stop. такие к случаям относятся преднамеренные атак типа "отказ в обслуживании" и операции ввода-вывода для которых thread.stop и thread.interrupt не работают должным образом.
Нижняя строка:
Убедитесь, что все потоки могут быть прерваны, или вам понадобятся конкретные знания о потоке - например, если у вас установлен флаг. Возможно, вы можете потребовать, чтобы задача была предоставлена вам вместе с кодом, необходимым для ее остановки, - определите интерфейс с помощью метода stop()
. Вы также можете предупредить, когда вам не удалось остановить задачу.
Ответ 6
Я думаю, вы должны взглянуть на правильные механизмы обработки параллелизма (потоки, запущенные в бесконечные циклы, не кажутся хорошими как таковые, кстати). Убедитесь, что вы немного читали о теме "убийство" или "остановка" темы.
То, что вы описываете, очень похоже на "рандеву", поэтому вы можете взглянуть на CyclicBarrier.
Могут быть другие конструкции (например, с использованием CountDownLatch, например), которые могут решить вашу проблему (один поток, ожидающий с тайм-аутом для защелки, другой должен отсчитывать защелку, если это заработало, что освободит ваш первый поток либо после тайм-аут или когда вызывается обратный отсчет защелки).
Обычно я рекомендую две книги в этой области: параллельное программирование в Java и Java на параллелизм на практике.
Ответ 7
Я создал вспомогательный класс только для этого некоторое время назад. Отлично работает:
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) {}
Ответ 8
Я размещаю вам фрагмент кода, который показывает способ решения проблемы.
Например, я читаю файл.
Вы можете использовать этот метод для другой операции, но вам нужно реализовать метод 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!");
}
}
}
Привет
Ответ 9
Одна вещь, о которой я не упоминал, заключается в том, что убийство потоков - это, как правило, плохая идея. Существуют методы для того, чтобы сделать поточные методы чисто отменными, но это отличается от простого уничтожения потока после таймаута.
Риск с тем, что вы предлагаете, заключается в том, что вы, вероятно, не знаете, в каком состоянии будет поток, когда вы его убьете, поэтому вы рискуете ввести нестабильность. Лучшее решение состоит в том, чтобы убедиться, что ваш потоковый код либо не зависает, либо прекрасно реагирует на запрос прерывания.
Ответ 10
Следующий фрагмент начнет операцию в отдельном потоке, а затем подождите до 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()
для объединения потоков без верхнего предела количества потоков.
Ответ 11
Вот мой действительно простой в использовании вспомогательный класс run или call часть кода 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;
}
}
Ответ 12
Отличный ответ BalusC's:
но просто добавьте, что сам тайм-аут не прерывает сам поток. даже если вы проверяете с помощью (! Thread.interrupted()) в своей задаче. если вы хотите, чтобы поток был остановлен, вы также должны убедиться, что future.cancel() вызывается, когда исключение тайм-аута является catch.
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!!");
}
}
}
Ответ 13
Я думаю, что ответ в основном зависит от самой задачи.
- Является ли это одной задачей снова и снова?
- Необходимо ли, чтобы таймаут прерывал текущую задачу сразу после ее истечения?
Если первый ответ - да, а второй - нет, вы можете сохранить его так просто, как это:
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 taking somewhat longer than it really does
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}
}
Если это не вариант, пожалуйста, сушите свои требования или покажите код.
Ответ 14
Я искал ExecutorService, который может прервать все пропущенные Runnables, выполненные им, но не нашел ни одного. Через несколько часов я создал один, как показано ниже. Этот класс может быть изменен для повышения надежности.
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();
}
Ответ 15
Теперь я встречаю такую проблему. Это происходит, чтобы декодировать картинку. Процесс декодирования занимает слишком много времени, чтобы экран оставался черным. 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);
Ответ 16
У меня была та же проблема. Поэтому я придумал простое решение, подобное этому.
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
}