【问题标题】: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() 调用...并非所有“阻塞”调用都如此,正如我试图在我的回答中指出的那样.您尝试中止的任务的细节对应该使用的方法产生了巨大的影响。有关该任务的更多信息会有所帮助。
  • 如果这些答案不能解决问题,那么我想更多的细节/代码应该有助于回答。
  • 你要限时的这些线程;他们是在进行阻塞调用,还是在某个循环中,您可以轻松地检查某个变量以查看是否该退出?

标签: java timeout multithreading timer


【解决方案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.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!!");
                  }          
                } 
            } 
            

            【讨论】:

              【解决方案7】:

              确实宁愿使用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!";
                  }
              }
              

              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 我尝试了这种方法试图终止我的线程,但无法使其工作。你可以在这里查看:stackoverflow.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 JavaJava 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) 以确定发生了什么。

                        【讨论】:

                          猜你喜欢
                          • 1970-01-01
                          • 1970-01-01
                          • 2021-05-27
                          • 1970-01-01
                          • 1970-01-01
                          • 2017-04-24
                          • 2016-06-03
                          • 1970-01-01
                          相关资源
                          最近更新 更多