如何使线程超时
Posted
技术标签:
【中文标题】如何使线程超时【英文标题】:How to timeout a thread 【发布时间】:2011-01-17 12:59:27 【问题描述】:我想运行一个线程一段固定的时间。如果它没有在那段时间内完成,我想杀死它,抛出一些异常,或者以某种方式处理它。怎么办?
我从this thread 发现的一种方法 就是在 Thread 的 run() 方法中使用 TimerTask。
有没有更好的解决方案?
编辑:添加赏金,因为我需要一个更清晰的答案。下面给出的 ExecutorService 代码不能解决我的问题。为什么我应该在执行后 sleep() (一些代码 - 我无法处理这段代码)?如果代码完成了,sleep()被打断了,怎么会是timeOut呢?
需要执行的任务不在我的控制范围内。它可以是任何一段代码。问题是这段代码可能会陷入无限循环。我不希望这种情况发生。所以,我只想在一个单独的线程中运行该任务。父线程必须等到该线程完成并需要知道任务的状态(即它是否超时或发生某些异常或是否成功)。如果任务进入无限循环,我的父线程会无限期地等待,这不是一个理想的情况。
【问题讨论】:
编辑:添加赏金,因为我需要更明确的答案。下面给出的 ExecutorService 代码不能解决我的问题。为什么我应该在执行我的代码后 sleep() ?如果代码完成了,sleep()被打断了,怎么会是timeOut呢?sleep()
只是代表“长时间运行的任务”的存根。只需将其替换为您的实际任务;)
... 一个“长时间运行的任务”,碰巧在其线程上响应 interrupt()
调用...并非所有“阻塞”调用都如此,正如我试图在我的回答中指出的那样.您尝试中止的任务的细节对应该使用的方法产生了巨大的影响。有关该任务的更多信息会有所帮助。
如果这些答案不能解决问题,那么我想更多的细节/代码应该有助于回答。
你要限时的这些线程;他们是在进行阻塞调用,还是在某个循环中,您可以轻松地检查某个变量以查看是否该退出?
【参考方案1】:
在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);
;
【讨论】:
【参考方案2】:我遇到了同样的问题。所以我想出了一个像这样的简单解决方案。
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;
保证 if 块没有在时间限制内执行。进程将终止并抛出异常。
示例:
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
【讨论】:
【参考方案3】:假设线程代码不在你的控制范围内:
来自上面提到的Javadocumentation:
如果线程没有响应 Thread.interrupt 怎么办?
在某些情况下,您可以使用特定于应用程序的技巧。例如, 如果一个线程正在等待一个已知的套接字,您可以关闭该套接字以 导致线程立即返回。不幸的是,真的有 不是任何通用的技术。 需要注意的是,在 等待线程没有响应的所有情况 Thread.interrupt,它也不会响应Thread.stop。这样 案例包括故意拒绝服务攻击和 I/O 操作 对于哪个 thread.stop 和 thread.interrupt 不能正常工作。
底线:
确保所有线程都可以被中断,否则您需要线程的特定知识——比如设置一个标志。也许您可以要求将任务连同停止它所需的代码一起提供给您 - 使用stop()
方法定义一个接口。您还可以在未能停止任务时发出警告。
【讨论】:
【参考方案4】:现在,我遇到了这样的问题。它碰巧解码图片。解码过程花费了太多时间,屏幕保持黑色。 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);
【讨论】:
【参考方案5】:我一直在寻找一个 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();
【讨论】:
【参考方案6】:BalusC 的精彩回答:
只是补充一点,超时本身不会中断线程本身。即使您在任务中使用 while(!Thread.interrupted()) 检查。如果要确保线程停止,还应确保在捕获超时异常时调用 future.cancel()。
package com.***.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!!");
【讨论】:
【参考方案7】:确实宁愿使用ExecutorService
而不是Timer
,这里是SSCCE:
package com.***.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!";
在Future#get()
方法中使用timeout
参数,例如将其增加到 5,您会看到线程完成。您可以在catch (TimeoutException e)
块中拦截超时。
更新:为了澄清概念上的误解,sleep()
不是必需的。它仅用于 SSCCE/演示目的。只需在此处代替sleep()
执行您的 长时间运行的任务。在您长时间运行的任务中,您应该检查线程是否不是interrupted,如下所示:
while (!Thread.interrupted())
// Do your long running task here.
【讨论】:
用其他一些长时间运行的语句替换Thread.sleep(4000)
,示例将不起作用。换言之,此示例仅在 Task
旨在理解 Thread.isInterrupted()
状态变化时才有效。
@BalusC 我尝试了这种方法试图终止我的线程,但无法使其工作。你可以在这里查看:***.com/questions/35553420/…
future.cancel(true) 引起的 InterruptedException 是如何处理的?
n 人对包名发表了评论,这是另一个 +1。吸收这种技能真是太好了。谢谢!
@BalusC 我有疑问,未来是否会同步执行,如果它需要超过预定义的时间,那么它将被终止。否则它会在未来的某个时间执行,同时我们正在指望时间......谢谢【参考方案8】:
这是我真正的简单易用帮助类run或call 一段 Java 代码 :-)
这是基于来自BalusC的优秀answer
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;
【讨论】:
【参考方案9】:前段时间我为此创建了一个辅助类。效果很好:
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)
【讨论】:
【参考方案10】:BalusC 说:
更新:为了澄清概念上的误解,不需要 sleep()。它仅用于 SSCCE/演示目的。只需在此处代替 sleep() 执行长时间运行的任务即可。
但是如果你将Thread.sleep(4000);
替换为for (int i = 0; i < 5E8; i++)
,那么它不会编译,因为空循环不会抛出InterruptedException
。
为了使线程可中断,它需要抛出一个InterruptedException
。
这对我来说似乎是一个严重的问题。我看不出如何调整这个答案来处理一般的长时间运行的任务。
编辑添加:我将此作为一个新问题重新提出:[interrupting a thread after fixed time, does it have to throw InterruptedException?]
【讨论】:
我的做法是在公共类我没有看到提到的一件事是杀死线程通常是一个坏主意。有一些技术可以使线程方法完全可中止,但这与在超时后终止线程不同。
您所建议的风险是您可能不知道当您杀死它时线程将处于什么状态 - 因此您可能会引入不稳定。更好的解决方案是确保您的线程代码不会自行挂起,或者能够很好地响应中止请求。
【讨论】:
如果没有上下文,像您这样的陈述听起来过于严格。在学术环境中,我经常需要在超时之前测试某些东西,当它发生时,我只需放弃所有计算并记录超时发生。可能在业内很少见,但仍然...... @AlessandroS:这是一个合理的观点,尽管 OP 要求“更好的解决方案”,我认为这意味着稳健性和可靠性优于蛮力。【参考方案12】:我向您发布了一段代码,它展示了如何解决问题的方法。 例如,我正在阅读一个文件。 你可以用这个方法进行其他操作,但是你需要实现 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!");
问候
【讨论】:
【参考方案13】:下面的 sn -p 将在单独的线程中启动一个操作,然后等待最多 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()
进行线程池,线程数不限。
【讨论】:
需要进口什么?SomeClass
和 Future
是什么?【参考方案14】:
我认为您应该看看适当的并发处理机制(运行到无限循环中的线程本身听起来并不好,顺便说一句)。确保您阅读了一些关于 "killing" or "stopping" Threads 主题的信息。
你所描述的,听起来很像“约会”,所以你可能想看看CyclicBarrier。
可能有其他构造(例如使用CountDownLatch)可以解决您的问题(一个线程等待闩锁超时,另一个线程如果已完成工作则应该倒计时闩锁,这将释放您的第一个线程在超时后或调用闩锁倒计时时)。
我通常推荐这方面的两本书:Concurrent Programming in Java和Java Concurrency in Practice。
【讨论】:
【参考方案15】:我认为答案主要取决于任务本身。
它是否一遍又一遍地执行一项任务? 是否有必要在超时后立即中断当前正在运行的任务?如果第一个答案是肯定的,而第二个答案是否定的,你可以这样简单:
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();
如果这不是一个选项,请缩小您的要求 - 或显示一些代码。
【讨论】:
【参考方案16】:对于任何旧任务都没有 100% 可靠的方法。编写任务时必须牢记这种能力。
像ExecutorService
这样的核心Java 库通过在工作线程上调用interrupt()
取消异步任务。因此,例如,如果任务包含某种循环,您应该在每次迭代时检查它的interrupt status。如果任务正在执行 I/O 操作,它们也应该是可中断的——而且设置它可能很棘手。无论如何,请记住代码必须主动检查中断;设置中断不一定有任何作用。
当然,如果您的任务是一些简单的循环,您可以在每次迭代时检查当前时间,并在指定的超时时间过去后放弃。在这种情况下不需要工作线程。
【讨论】:
根据我的经验,唯一没有对开始中断做出反应的代码是阻塞本机代码(等待操作系统)。 @ThorbjørnRavnAndersen 我同意,但这是很多代码。我的观点是没有通用的机制。您必须了解任务的中断策略。 @erickson,我同意你的看法。直截了当的回答,必须为每个任务定义一个取消策略,如果您有兴趣以它的方式停止它。或者线程应该知道它被中断时应该做什么。毕竟,中断和停止任何线程只是目标线程可能接受或拒绝的请求,因此最好在编写任务时牢记这一点。 不能 executorservice 选择在调用线程上运行任务吗? executorservice 也可能会选择在未来的某个时间执行任务? @user1232726 父接口的execute()
方法Executor
可以在调用线程中运行一个任务。对于返回 Future
实例的 ExecutorService
的 submit()
方法没有类似的声明。该服务的含义是必须通过关闭清理工作线程,并且任务是异步执行的。也就是说,合同中没有任何内容表明禁止ExecutorService
在提交线程中执行任务;这些保证来自实现 API,例如 Executors
factory。【参考方案17】:
考虑使用ExecutorService 的实例。 invokeAll()
和 invokeAny()
方法都可以使用 timeout
参数。
当前线程将阻塞,直到方法完成(不确定这是否可取),因为任务正常完成或达到超时。您可以检查返回的Future
(s) 以确定发生了什么。
【讨论】:
以上是关于如何使线程超时的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 executor.execute 和 future.get() 结束任务(使线程超时)(通过上升中断)而不阻塞主线程
如何使 libcurl C++ 调用超时和/或知道调用中何时发生超时