超时

时间:2016-12-10 06:49:18

标签: java concurrency forkjoinpool

我正在尝试使用ForkJoinPool中的awaitQuiescence方法等待所有提交的任务完成,如果超时后任务尚未完成,则返回false。

实际上,所有提交的任务都可以向池中添加其他任务,因此我无法使用awaitTermination方法,因为这会阻止提交其他任务。 但是,即使指定的时间结束,awaitQuiescence也不会返回任何内容。

我试图在下面的代码中明确这个问题。永远不会触发CountDownLatch.await,但为什么awaitQuiescence方法不返回false?

public static void main(String[] args) {
    final ForkJoinPool test = new ForkJoinPool(1,
            ForkJoinPool.defaultForkJoinWorkerThreadFactory, null,true);
    final CountDownLatch latch = new CountDownLatch(1);

    test.execute(() -> { 
            try {
                System.out.println("Sleeping");
                Future<Double> f = test.submit(() -> { 
                        latch.await(); 
                        return 0d; 
                    });
                System.out.println(f.get());
                System.out.println("Waking up");
    } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            });

    System.out.println(test.awaitQuiescence(1, TimeUnit.SECONDS));
} 

非常感谢!

1 个答案:

答案 0 :(得分:0)

  

为什么awaitQuiescence方法不返回false?

似乎awaitQuiescence在有待处理的任务时忽略timeout并在调用者的线程中执行任务(请参阅source code)。

线程转储:

"ForkJoinPool-1-worker-1" [...] Object.wait() [...]
   java.lang.Thread.State: WAITING (on object monitor)
  [...]
  at java.util.concurrent.ForkJoinTask.get(ForkJoinTask.java:995)
  [...]
  at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)

[...]

"main" [...] waiting on condition [...]
   java.lang.Thread.State: WAITING (parking)
  [...]
  at java.util.concurrent.CountDownLatch.await(CountDownLatch.java:231)
  [...]
  at java.util.concurrent.ForkJoinTask$AdaptedCallable.exec(ForkJoinTask.java:1445)
  at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
  at java.util.concurrent.ForkJoinPool.awaitQuiescence(ForkJoinPool.java:3097)
  [...]

&#34;主&#34;线程执行第二个任务并等待锁存,因此awaitQuiescence永远不会终止。

在我看来这是一个错误。基于javadoc我假设方法的最大运行时间(&#34;等待的最长时间&#34;)大约为timeout,但上限实际上更多比如所有待处理任务及其所有后续任务的执行时间&#34; (可能除了终端之外)。

另一方面,FJ池并不适用于此类任务(使用非池管理同步)。来自ForkJoinTask的javadoc

  

理想情况下,计算应避免同步方法或块,以及   应尽量减少其他阻止同步,除了加入   其他任务或使用广告的Phasers等同步器   配合fork / join调度。

[...]

  

可以定义和使用可能阻止的ForkJoinTasks,但是   这样做需要三个进一步的考虑:(1)完成少数   如果任何其他任务应该依赖于阻止的任务   外部同步或I / O.事件样式的异步任务   从不加入(例如,那些子类化CountedCompleter)   属于这一类。 (2)为了尽量减少资源影响,任务应该   小;理想情况下只执行(可能)阻止操作。 (3)   除非使用ForkJoinPool.ManagedBlocker API,否则数量为   已知可能被阻止的任务比游泳池少   ForkJoinPool.getParallelism()级别,池不能保证   足够的线程将可用于确保进展或良好   性能

考虑使用ThreadPoolExecutor和/或模拟awaitQuiescence(例如使用Phaser)。可能实施的草图:

class TaskTrackingExecutorService implements ExecutorService {

  private final ExecutorService delegate;
  private final Phaser taskTracker = new Phaser();

  public TaskTrackingExecutorService(ExecutorService delegate) {
    this.delegate = delegate;
  }

  @Override
  public <T> Future<T> submit(Callable<T> task) {
    return delegate.submit(() -> {
      taskTracker.register();
      try {
        return task.call();
      } finally {
        taskTracker.arriveAndDeregister();
      }
    });
  }

  @Override
  public void execute(Runnable command) {
    submit(Executors.callable(command));
  }

  public boolean awaitQuiescence(long timeout, TimeUnit timeUnit) throws InterruptedException {
    taskTracker.register();
    try {
      taskTracker.awaitAdvanceInterruptibly(taskTracker.arriveAndDeregister(), timeout, timeUnit);
      return true;
    } catch (TimeoutException e) {
      return false;
    }
  }

  @Override
  public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
    return delegate.awaitTermination(timeout, unit);
  }

  // rest is similar: either use submit method or the delegate.

}


public class Test {
  public static void main(String[] args) throws InterruptedException {
    TaskTrackingExecutorService pool =
        new TaskTrackingExecutorService(Executors.newCachedThreadPool());
    CountDownLatch latch = new CountDownLatch(1);

    pool.execute(() -> {
          System.out.println("Sleeping");
          Future<Double> f = pool.submit(() -> {
            latch.await();
            return 0d;
          });
          try {
            System.out.println(f.get());
          } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
          }
          System.out.println("Waking up");
        }
    );

    System.out.println(pool.awaitQuiescence(2, TimeUnit.SECONDS));
  }
}