【问题标题】:Java - Timeout long running Callable ThreadJava - 超时长时间运行的可调用线程
【发布时间】:2017-11-07 16:00:27
【问题描述】:
public class FutureGetTimeoutTest {

    private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(5);

    public static void main(String[] args) throws InterruptedException, ExecutionException {

        List<String> respList = new ArrayList<String>();

        List<Future<String>> futures = new ArrayList<Future<String>>();
        futures.add(THREAD_POOL.submit(new CallableTask(1L)));
        futures.add(THREAD_POOL.submit(new CallableTask(2L)));
        futures.add(THREAD_POOL.submit(new CallableTask(3L)));

        long start = System.currentTimeMillis();
        System.out.println(start);
        for (Future<String> future : futures) {
            try {
                respList.add(future.get(10000, TimeUnit.MILLISECONDS));
                /*
                 * Timeout time for 2nd Task starts only at the end of 1st Task Timeout
                 * and so 2nd task is able to run for 20s and 3rd task for 30s!
                 */
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }
        long end = System.currentTimeMillis();
        System.out.println(end);
        System.out.println(end - start);
        System.out.println(respList);

    }

}

class CallableTask implements Callable<String> {

    private long ipAddressL;

    public CallableTask(long ipAddressL) {
        this.ipAddressL = ipAddressL;
    }

    @Override
    public String call() throws Exception {

        if (ipAddressL == 1) {
            Thread.sleep(10000);
            /* Imagine a DB operation taking more time. */
            return "1";
        } else if (ipAddressL == 2) {
            Thread.sleep(20000);
            return "2";
        } else {
            Thread.sleep(30000);
            return "3";
        }

    }
}

如果每个任务单独花费超过 10 秒,我想返回一个空字符串或只是终止线程或从每个任务中抛出 TimeoutException。

假设第一个线程需要 10 秒,Future.get() 将等待 10 秒然后超时。我将捕获异常并继续迭代第二个未来对象。假设此时第二个线程尚未完成(这意味着它运行了 10 秒,而第一个线程运行并仍在运行),现在第二个线程上的 Future.get() 将再等待 10 秒,因此后续线程总共等待 20 秒,依此类推.

future.get(1000, TimeUnit.MILLISECONDS) (1 sec), 

将确保整个操作的时间限制为 10 秒,但我需要通过对每个单独的并发任务设置 10 秒的限制来对整个操作进行 10 秒的限制。

【问题讨论】:

  • 您是否研究过ConnectionStatement 对象上可用的网络超时设置?您不能显式取消查询,但可以捕获 SQLTimeOut 异常。
  • 不知道为什么不赞成,但也许代码示例可以最小化并变得更抽象。不过,请始终感谢投反对票,一个遗憾的投反对票会给您带来多达 5 票投反对票的分数,并且您很可能会在不应该投反对票的问题上得到他们。至于问题,Jim 是对的,设置你的 sql 超时并捕获它抛出的异常。
  • 为什么要投反对票? SQL 在 jar 库中。我无法修改或任何连接参数。我只能访问线程。我得到的最接近的是 Future.get() 的超时。但主要问题是第一个 Future.get() 的超时将影响后续的 Future.get() - 这意味着第二个线程可以运行比 Future 中提到的超时更长的时间,因为它的超时时间仅在第一个 Future 超时后开始。如何从内部终止线程,对超时或任何其他外部机制进行自我监控?

标签: java multithreading future executorservice callable


【解决方案1】:

使用 THREAD_POOL.invokeAll 而不是 submit 等待 10 秒以完成任务。

如果某些任务在 10 秒内完成,您可以使用future.isDone() 进行检查,并使用future.get 检索结果而不阻塞。

【讨论】:

  • 如果at least one task 花费的时间超过超时,您的建议是超时。我可能经常让其中一项任务长期运行。因此,我不想拒绝其他短期任务的结果。我需要的是每个单独任务的超时,并且只忽略长时间运行的超时任务的结果 - 并且需要这个单独的超时是绝对的,而不是像Future.get() 中的相对超时。这确保了所花费的总时间也少于 10 秒 approximately,更重要的是,我可以利用短期任务的结果而不是全部丢弃。
  • invokeAll 将阻塞主线程,直到所有任务完成。让我尝试在主线程中使用CountDownLatch,在submit-ing 所有可调用任务之后超时。迭代并检查future.isDoneget 只有isDone 为真。
  • 我说的是这个:invokeAll(Collection&lt;? extends Callable&lt;T&gt;&gt; tasks, long timeout, TimeUnit unit)
  • 我想如果invokeAll超时,我会失去所有的结果。但是长时间运行的线程(我无法控制的 DB-Infra jar 库中的 DB 读取查询)会在超时后立即停止吗?我想,不,必须明确处理中断。如果是循环,则可以在每次迭代时检查中断。但是如果线程在等待一个DB读取结果,如何实现中断呢?这将是一个问题,因为我的ExecutorService \ ThreadPool 将很快达到其极限(所以我必须创建一个大的线程池大小?)。
  • 您不会丢失结果。请参阅invokeAll(Collection,long,TimeUnit 的 javadocs,具体而言:返回代表任务的 Futures 列表,其顺序与给定任务列表的迭代器生成的顺序相同。如果操作没有超时,则每个任务都已完成。如果确实超时,其中一些任务将无法完成。
猜你喜欢
  • 1970-01-01
  • 2021-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-22
相关资源
最近更新 更多