我想运行一个线程一段固定的时间。如果在该时间内没有完成,我想要终止它,抛出一些异常,或者以某种方式处理它。怎样才能做到呢?

我从这篇文章中找到了一种方法 是在线程的run()方法中使用一个TimerTask。

有没有更好的解决方案?

  编辑:添加赏金,因为我需要一个更明确的答案。下面给出的ExecutorService代码没有解决我的问题。为什么我应该在执行(一些代码-我没有处理这段代码)后睡觉()?如果代码完成并且sleep()被中断,那怎么可能是timeOut呢?

The task that needs to be executed is not in my control. It can be any piece of code. The problem is this piece of code might run into an infinite loop. I don't want that to happen. So, I just want to run that task in a separate thread. The parent thread has to wait till that thread finishes and needs to know the status of the task (i.e whether it timed out or some exception occured or if its a success). If the task goes into an infinite loop, my parent thread keeps on waiting indefinitely, which is not an ideal situation.


当前回答

现在,我遇到了这样的问题。它恰好解码图片。解码过程耗时太长,导致屏幕黑屏。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);

其他回答

考虑使用ExecutorService的实例。invokeAll()和invokeAny()方法都有一个超时参数。

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

下面的代码片段将在单独的线程中启动一个操作,然后等待最多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()进行线程池,没有线程数量的上限。

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!!");
      }          
    } 
} 

我正在寻找一个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();
}

对于任何旧任务,都没有100%可靠的方法来做到这一点。在编写任务时必须考虑到这种能力。

像ExecutorService这样的核心Java库通过工作线程上的interrupt()调用取消异步任务。因此,例如,如果任务包含某种类型的循环,您应该在每次迭代中检查它的中断状态。如果任务执行的是I/O操作,那么它们也应该是可中断的——设置这一点可能很棘手。在任何情况下,请记住代码必须主动检查中断;设置中断并不一定会起任何作用。

当然,如果您的任务是一些简单的循环,您可以在每次迭代中检查当前时间,并在指定的超时时间过去时放弃。在这种情况下不需要工作线程。