Most efficient Java executor service for depth-first asynchronous processing of a tree

Viewed 54

Let's say I have a tree like this.

A
| \
B1 B2
|
C

And let's say I want to asynchronously process the tree in Java, but I want to asynchronously process the children at each level before the parent. So I would have asynchronouslyProcess(A) which would asynchronously get the children of A and call asynchronouslyProcess(B1) and asynchronouslyProcess(B2), but during asynchronouslyProcess(B1) it would call asynchronouslyProcess(C). i.e. Each level has to wait until the children are processed.

Simplified I'm using CompletableFuture.supplyAsync() to get the children of each tree node (which requires I/O), and .thenApplyAsync() to process all the children (which also requires I/O), recursively. So when we are processing level C, we have a chain of something like this:

  1. CompletableFuture.supplyAsync(/* process A */)
  2. CompletableFuture.supplyAsync(/* get children of A */)
  3. .thenApplyAsync(/* process B1 */)
  4. CompletableFuture.supplyAsync(/* get children of B1 */)
  5. .thenApplyAsync(/* process C */)

Thus there will be lots of tasks that are asynchronously nested, and I could imagine that many of them will be blocking on I/O at any given time. I want to process the tree in parallel as quickly as possible. Which Java executor service would be most appropriate? Here are some choices. (I know there is a newWorkStealingPool() method that defaults to using available processors; I included the value explicitly for clarity.)

  • Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors())
  • newWorkStealingPool(Runtime.getRuntime().availableProcessors()) (FIFO)
  • new ForkJoinPool(Runtime.getRuntime().availableProcessors(), ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, false) (LIFO)
  • newCachedThreadPool()

Here are my doubts making the decision:

  • If I have a lot of blocking on I/O, will having a cached thread pool be of any benefit with its ability to create new threads? Or will the threads blocking on I/O in a fixed thread pool be able to attend to other tasks backed up on the queue for e.g. other children?
  • If there are a lot of threads blocking on I/O, would it be better to have a fork/join thread pool with its separate queues?
  • Since the children must be processed first, would a LIFO queue be more appropriate? Or would a work stealing pool that allows other threads to steal the child tasks from the tail be more efficient?
0 Answers
Related