使用ThreadPoolExecutor使用CompletableFuture时,在各个线程上设置超时

时间:2018-03-20 20:04:22

标签: java multithreading completable-future

我有一个程序,它使用ThreadPoolExecutor异步执行。我在Java 8中使用CompletableFutures来安排这些任务,然后让它们由线程池中可用的线程执行。

我的代码如下所示:

public class ThreadTest {

    public void print(String m) {
        System.out.println(m);
    }

    public class One implements Callable<Integer> {

        public Integer call() throws Exception {
            print("One...");
            Thread.sleep(6000);
            print("One!!");
            return 100;
        }
    }

    public class Two implements Callable<String> {

        public String call() throws Exception {
            print("Two...");
            Thread.sleep(1000);
            print("Two!!");
            return "Done";
        }
    }

    @Test
    public void poolRun() throws InterruptedException, ExecutionException {
        int n = 3;
        // Build a fixed number of thread pool
        ExecutorService pool = Executors.newFixedThreadPool(n);

        CompletableFuture futureOne = CompletableFuture.runAsync(() -> new One());
        // Wait until One finishes it's task.
        CompletableFuture futureTwo = CompletableFuture.runAsync(() -> new One());
        // Wait until Two finishes it's task.
        CompletableFuture futureTwo = CompletableFuture.runAsync(() -> new Two());

        CompletableFuture.allOf(new CompletableFuture[]{futureOne, futureTwo, futureThree}).get();
        pool.shutdown();
    }
}

我需要在每个单独的线程上设置超时,例如在10分钟时超时。 我查看了CompletableFuture的.get(TimeUnit timeUnit)方法,但我不确定是否在线程池或单个线程本身设置了超时。

https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/CompletableFuture.html#get-long-java.util.concurrent.TimeUnit-

或者我应该改变使用执行程序服务在各个线程上设置超时的方式?

谢谢!

2 个答案:

答案 0 :(得分:0)

您所指的get方法仅指您等待个人未来的时间。默认情况下,即使超时,任务也将继续执行。

如果你想要暂停执行的线程超时(这样该线程可以执行其他操作),如果遇到cancel,则需要TimeoutException该任务。例如:

    try {
        result = future.get(10, TimeUnit.MINUTES);
    } catch (TimeoutException e) {
        future.cancel(true);
    }

注意:这假定您有一个定期检查中断的任务。 IO绑定任务通常会执行此操作,但CPU绑定任务通常需要显式检查中断的线程,以使取消生效。

答案 1 :(得分:0)

CompletableFuture.get不会停止运行任务的线程。只要您指定结果,调用线程就会等待,如果超时,它将抛出异常。但是运行任务的线程将一直持续到完成为止。 这是潜在的现实:Java不会允许您随意终止任务。曾经有一段时间这是API的一部分,Thread类上有Thread.suspend / resume / stop方法。 这些已被弃用,因为无法知道挂起或停止的线程是否持有可能阻止执行其他线程的锁。因此,在任意时间和地点停止线程本质上是不安全的。你的程序最终会出现僵局。

见这里:https://docs.oracle.com/javase/8/docs/technotes/guides/concurrency/threadPrimitiveDeprecation.html

相同的参数适用于您在并发程序包中找到的任何池和执行程序以及其他类。你不能随意停止一个线程或任务。

您必须将停止和完成的逻辑放入任务本身,或者您只需等待它完成。在这种情况下,你有一个运行一秒钟,一个运行6秒钟。你可以使用互斥锁和信号量之类的东西,你可以使用并发包和concurrent.locks包中的许多东西,这些都可以用来协调线程并传递它们的位置信息。

但是你不会在任何地方找到一个允许你在任何时间点杀死任意线程的方法,除了上面列出的那些之前已经弃用的那些,并鼓励你远离这些方法。

Future.cancel将阻止一个任务启动,它将尝试中断正在运行的任务,但是所有它都会阻止一个线程(通过使其抛出InterruptedException)当前在可中断的方法调用上被阻止像Thread.sleep(),Object.wait()或Condition.await()。如果您的任务正在执行任何其他操作,则在完成任务或调用可中断的方法调用之前不会停止。

这将适用于上面的代码,因为您正在调用Thread.sleep。但是一旦你的任务完成了工作,就会按照我的描述行事。