CompletableFuture with PriorityBlockingQueue

Robert Engel :

I am trying to write a Single Thread Executor that returns a CompletableFuture when a task gets scheduled and executes the task based on a PriorityBlockingQueue.

My Tasks look like this:

  public interface DriverTask<V> {

    V call(WebDriver driver) throws Throwable;

    default TaskPriority getPriority() {
      return TaskPriority.LOW;
    }
  }

  public enum TaskPriority {
    HIGH,
    MEDIUM,
    LOW
  }

Now my problem is when I use the CompletableFuture.supplyAsync Method the Executor only gets a Runnable and I dont know how to get my Executor to know the priority of the original Task.

Is there a different way to create a CompletableFuture so that I get execute them based on priority?

Holger :

The principle of methods like supplyAsync is to create a new CompletableFuture instance an setting up and asynchronous job which will eventually complete the future.

You can do the same for your nontrivial setup:

private static WebDriver currentDriver() {
    …
}
private static final ExecutorService BACKEND
    = new ThreadPoolExecutor(1, 1, 1, TimeUnit.MINUTES, new PriorityBlockingQueue<>());

public static <V> CompletableFuture<V> runAsync(DriverTask<V> dt) {
    CompletableFuture<V> result = new CompletableFuture<>();
    class Job implements Runnable, Comparable<Job>,
                         CompletableFuture.AsynchronousCompletionTask {
        public void run() {
            try {
                if(!result.isDone()) result.complete(dt.call(currentDriver()));
            }
            catch(Throwable t) { result.completeExceptionally(t); }
        }
        private TaskPriority priority() { return dt.getPriority(); }
        public int compareTo(Job o) { return priority().compareTo(o.priority()); }
    }
    BACKEND.execute(new Job());
    return result;
}

Note that implementing CompletableFuture.AsynchronousCompletionTask is not necessary; it’s just a convention for marking those Runnable implementations whose purpose is to complete a CompletableFuture.

Another advantage of implementing the logic yourself is that this first stage does not need to wrap exceptions in a CompletionException. So when the caller chains an exceptionally, it will see the original, unwrapped exception. Also, a caller of join would get a CompletionException reflecting the code location of the join call with the original exception as the cause, carrying much more useful information.

The purpose of if(!result.isDone()) before the actual completion attempt is to skip it if the CompletableFuture has been cancelled (or otherwise completed) while waiting in the queue. Once the completion attempt has been started, canceling won’t interrupt it. This is the general behavior of CompletableFuture.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=3699&siteId=1