如何使线程超时

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】:

这是我真正的简单易用帮助类runcall 一段 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 &lt; 5E8; i++) ,那么它不会编译,因为空循环不会抛出InterruptedException

为了使线程可中断,它需要抛出一个InterruptedException

这对我来说似乎是一个严重的问题。我看不出如何调整这个答案来处理一般的长时间运行的任务。

编辑添加:我将此作为一个新问题重新提出:[interrupting a thread after fixed time, does it have to throw InterruptedException?]

【讨论】:

我的做法是在公共类调用方法中添加一个“抛出异常”【参考方案11】:

我没有看到提到的一件事是杀死线程通常是一个坏主意。有一些技术可以使线程方法完全可中止,但这与在超时后终止线程不同。

您所建议的风险是您可能不知道当您杀死它时线程将处于什么状态 - 因此您可能会引入不稳定。更好的解决方案是确保您的线程代码不会自行挂起,或者能够很好地响应中止请求。

【讨论】:

如果没有上下文,像您这样的陈述听起来过于严格。在学术环境中,我经常需要在超时之前测试某些东西,当它发生时,我只需放弃所有计算并记录超时发生。可能在业内很少见,但仍然...... @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()进行线程池,线程数不限。

【讨论】:

需要进口什么? SomeClassFuture 是什么?【参考方案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 实例的 ExecutorServicesubmit() 方法没有类似的声明。该服务的含义是必须通过关闭清理工作线程,并且任务是异步执行的。也就是说,合同中没有任何内容表明禁止ExecutorService 在提交线程中执行任务;这些保证来自实现 API,例如 Executors factory。【参考方案17】:

考虑使用ExecutorService 的实例。 invokeAll()invokeAny() 方法都可以使用 timeout 参数。

当前线程将阻塞,直到方法完成(不确定这是否可取),因为任务正常完成或达到超时。您可以检查返回的Future(s) 以确定发生了什么。

【讨论】:

以上是关于如何使线程超时的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 executor.execute 和 future.get() 结束任务(使线程超时)(通过上升中断)而不阻塞主线程

如何使 libcurl C++ 调用超时和/或知道调用中何时发生超时

如何从主线程超时java线程?

如何使用 Docker 使 phpmyadmin 持续更长时间而不会超时

如何在 timeLimit 之后使未来的计算超时?

如何使 ASP.NET MVC 登录用户超时?