【问题标题】:Java ExecutorService usageJava ExecutorService 使用
【发布时间】:2018-09-07 11:19:45
【问题描述】:

我想制作一个 2 线程应用程序,如下所示:

  1. 创建2个线程池
  2. 在 #1 线程上运行 foo 方法
  3. 等待 2 秒
  4. 在 #2 线程上运行 bar 方法
  5. 2 线程池最多应等待 20 秒

    ExecutorService executor = Executors.newFixedThreadPool(2);
    executor.submit(() -> foo());
    executor.awaitTermination(2, TimeUnit.SECONDS);
    executor.submit(() -> bar());
    if (!executor.awaitTermination(20, TimeUnit.SECONDS))
        executor.shutdownNow();
    

它不工作,有什么问题?

【问题讨论】:

  • 如果你想要求在单独的线程上执行,那为什么要使用线程池呢?另外,它怎么不起作用?另请注意,awaitTermination 并不意味着 等待 时间流逝。
  • 究竟是什么不起作用?如果你想让事情发生在特定线程上,你为什么要使用线程池(这个在 #1 上,那个在 #2 上)。
  • 没必要跑在不同的线程池上,主要是并行运行,所以可以在同一个线程上。
  • @ernest_k 如何使用 ExecutorService 等待预定义的秒数?
  • 我要问,为什么要等2秒?

标签: java multithreading executorservice


【解决方案1】:

好问题!我认为您对awaitTermination() 方法没有清楚的了解。让我们看看这个document

在关闭请求后阻塞,直到所有任务完成执行,或发生超时,或当前线程被中断,以先发生者为准。

awaitTermination() 不应用于等待所有任务完成。如果这是您正在寻找的语义,请使用invokeAll() 阻止直到所有任务完成。使用awaitTermination() 没有之前的shutdown() 是不正确的,并且可能由于awaitTermination 未关闭执行程序而导致您等待一段时间。

在这种情况下,您只是试图强制应用程序等待您的线程完成,但如果它不在您分配的时间范围内,您希望继续。这个具体案例让我对多线程的使用产生了一些疑问,因为它似乎有点过度设计,但出于教育目的,让我们解释一下如何做到这一点。

基本解决方案

ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> foo());
// Thread.sleep(2000); // Wait for 2 seconds
TimeUnit.SECONDS.sleep(2); // Also waits for 2 seconds and a bit more readable
executor.submit(() -> bar());
if (!executor.awaitTermination(20, TimeUnit.SECONDS))
    executor.shutdownNow();

虽然这不是理想的代码,但它会给你你正在寻找的语义。

改进

executor.submit() 将返回一个 Future<T> 对象。这是 Java 中非常强大的对象,具有很多功能。让我们看看我们如何改进这个场景。

ExecutorService executor = Executors.newFixedThreadPool(2);
Future<T> foo = executor.submit(() -> foo());
T result = foo.get(2, TimeUnit.SECONDS); // Attempt to grab the result for 2 seconds, then move on
executor.submit(() -> bar());
if (!executor.awaitTermination(20, TimeUnit.SECONDS))
    executor.shutdownNow();

现在,这是更合适的代码,并且会以更好的代码结构为您提供所需的语义。此外,如果第一个未来在 2 秒之前完成,它将提前解除阻塞,比基本解决方案有所改进!

正确关机

假设您想等待 20 秒以等待 executor 完成,您可以执行以下操作作为额外改进。使用以下代码进行关机,因为它来自ExecutorService 的文档作为关机示例。在这里,我已将您的等待时间更新为 20 秒。

pool.shutdown(); // Disable new tasks from being submitted
try {
    // Wait a while for existing tasks to terminate
    if (!pool.awaitTermination(20, TimeUnit.SECONDS)) {
      pool.shutdownNow(); // Cancel currently executing tasks
      // Wait a while for tasks to respond to being cancelled
      if (!pool.awaitTermination(20, TimeUnit.SECONDS))
          System.err.println("Pool did not terminate");
    }
} catch (InterruptedException ie) {
    // (Re-)Cancel if current thread also interrupted
    pool.shutdownNow();
    // Preserve interrupt status
    Thread.currentThread().interrupt();
}

【讨论】:

  • awaitTermination 不等待任务完成。正如它的名字所说,它等待终止。如果你没有在它之前调用shutDown,那么在指定时间内永远不会终止,所以它只是sleep的混淆形式。
  • @Holger 为了清楚起见,让我稍微重构一下我的答案
  • 我尝试了第一个解决方案,但它不起作用:当它应该休眠 2 秒时却没有。即使 foo 已经返回,它也总是在每种情况下等待 20 秒。
  • @Twi 第一个解决方案是一种天真的方法,即使 foo 返回了也会一直等待。第二种解决方案是更好的方法,应该使用。您需要重构您的关闭以模仿提供的关闭。
【解决方案2】:

使用预定的执行器服务怎么样?你想运行任务a,你想运行任务b,你想在20秒后关闭执行器服务。

ScheduledExecutorService service = Executors.newScheduledThreadPool(2);
Future<?> futureA = service.submit(()->foo());
ScheduledFuture<?> futureB = service.schedule(()->bar(), 2, TimeUnit.SECONDS);

不知道你为什么要关闭executor,但是你可以使用future来等待任务完成。

Object b = futureB.get(20, TimeUnit.SECONDS);
service.shutdown();

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-07
    • 2014-11-15
    • 2010-09-29
    • 1970-01-01
    • 2016-09-23
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    相关资源
    最近更新 更多