Original article, please indicate the source for reprint: http://abc08010051.iteye.com/blog/2409693
I will revise it later to make the article more readable. The current version is still relatively rough.
CompletableFuture is a new class provided by java 1.8. It is an enhancement to Future. It absorbs the characteristics of guava asynchronous threads and can realize a series of asynchronous thread operations. There are many blogs on the Internet for many conventional usages. Here are some code implementations:
This is the basic structure of CompletableFuture
CompletableFuture base property methods
volatile Object result; // Either the result or boxed AltResult volatile Completion stack; // Top of Treiber stack of dependent actions
result is used to store the result returned by the thread
The stack behavior is a function of a stack. It is used to store the actions to be executed. This is useless when a single asynchronous thread returns. It is only useful when multiple threads are waiting.
Completion basic property method
volatile Completion next; // Treiber stack link abstract CompletableFuture<?> tryFire(int mode);
next chain structure, store the next
tryFire method, which mainly returns a dependent Completion
Glossary:
Dependent: Dependency, when multiple threads operate, how to wait for each thread to complete before returning, mainly depends on this dependency, and the dependent postComplete() method will be called to pass upwards before it is processed
source: source, CompletableFuture of the user-defined thread
1 single use
/** * Returns a new CompletableFuture that is asynchronously completed * by a task running in the {@link ForkJoinPool#commonPool()} with * the value obtained by calling the given Supplier. * * @param supplier a function returning the value to be used * to complete the returned CompletableFuture * @param <U> the function's return type * @return the new CompletableFuture */ public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) { return asyncSupplyStage(asyncPool, supplier); } static <U> CompletableFuture<U> asyncSupplyStage(Executor e, Supplier<U> f) { if (f == null) throw new NullPointerException(); CompletableFuture<U> d = new CompletableFuture<U>(); e.execute(new AsyncSupply<U>(d, f)); return d; } static final class AsyncSupply<T> extends ForkJoinTask<Void> implements Runnable, AsynchronousCompletionTask { CompletableFuture<T> dep; Supplier<T> fn; AsyncSupply(CompletableFuture<T> dep, Supplier<T> fn) { this.dep = dep; this.fn = fn; } public final Void getRawResult() { return null; } public final void setRawResult(Void v) {} public final boolean exec() { run(); return true; } public void run() { //CompletableFuture handle, put the return value of Supplier into the result property of CompletableFuture, the execution of the current thread is executed in the default thread pool, which can be obtained externally CompletableFuture<T> d; Supplier<T> f; if ((d = dep) != null && (f = fn) != null) { dep = null; fn = null; if (d.result == null) { try { d.completeValue(f.get()); } catch (Throwable ex) { d.completeThrowable(ex); } } d.postComplete(); } } } /** * Pops and tries to trigger all reachable dependents. Call only * when known to be done. */ final void postComplete() { /* * On each step, variable f holds current dependents to pop * and run. It is extended along only one path at a time, * pushing others to avoid unbounded recursion. */ CompletableFuture<?> f = this; Completion h; / / Loop through the stack property of CompletableFuture, Completion is a chain operation, if there is a next one, trigger the tryFire method of the next Completion while ((h = f.stack) != null || (f != this && (h = (f = this).stack) != null)) { CompletableFuture<?> d; Completion t; if (f.casStack(h, t = h.next)) { if (t != null) { if (f != this) { pushStack(h); continue; } h.next = null; // detach } f = (d = h.tryFire(NESTED)) == null ? this : d; } } }
Waiting to get the result :
public T get() throws InterruptedException, ExecutionException { Object r; return reportGet((r = result) == null ? waitingGet(true) : r); } private Object waitingGet(boolean interruptible) { Signaller q = null; boolean queued = false; int spins = -1; Object r; //Get the result property in a loop, judge whether it is empty, get the result if it is not empty, and jump out of the while loop while ((r = result) == null) { if (spins < 0) //If multiple threads are allowed, assign a value of 256 to spins, and then decrement in a loop. If there is no return value at this time, take the following else branch spins = (Runtime.getRuntime().availableProcessors() > 1) ? 1 << 8 : 0; // Use brief spin-wait on multiprocessors else if (spins > 0) { if (ThreadLocalRandom.nextSecondarySeed() >= 0) --spins; } else if (q == null) //Create a thread waiting for the signal q = new Signaller(interruptible, 0L, 0L); else if (!queued) //Replace the stack attribute and assign the result of whether the replacement is successful to queued queued = tryPushStack(q); else if (interruptible && q.interruptControl < 0) {//Allow interrupt, and q.interruptControl = 1, this branch will not be taken, the following loop will take this branch when thread interruption occurs q.thread = null; cleanStack(); return null; } else if (q.thread != null && result == null) {//If the result is not returned, it will enter the current branch try { //The loop judges whether q is released or not, and waits until the Signaller release conditions are met (mainly to judge whether it times out). In the above construction method of the Signaller, the deadline is 0, and it will not be released due to timeout. It will only be released when the thread is interrupted. ForkJoinPool.managedBlock(q); } catch (InterruptedException ie) {//If a thread is interrupted, set Signaller's interruptControl to -1, and wait until the next cycle to use q.interruptControl = -1; } } } if (q != null) {//The signal thread is not null, if the Signaller's interrupt control flag bit is less than 0, return null or the thread is interrupted q.thread = null; if (q.interruptControl < 0) { if (interruptible) r = null; // report interruption else Thread.currentThread().interrupt(); } } //Pass to the next Completion, if not, do not execute postComplete(); return r; } private static <T> T reportGet(Object r) throws InterruptedException, ExecutionException { // Wrap the return value according to different situations if (r == null) // by convention below, null means interrupted throw new InterruptedException(); if (r instanceof AltResult) { Throwable x, cause; if ((x = ((AltResult)r).ex) == null) return null; if (x instanceof CancellationException) throw (CancellationException)x; if ((x instanceof CompletionException) && (cause = x.getCause()) != null) x = cause; throw new ExecutionException(x); } @SuppressWarnings("unchecked") T t = (T) r; return t; }
2 Wait for multiple threads to complete execution before returning
//demo , stageRunnable is a variable that implements the Runnable type CompletableFuture future = CompletableFuture.allOf(CompletableFuture.runAsync(stageRunnable), CompletableFuture.runAsync(stageRunnable), CompletableFuture.runAsync(stageRunnable)); System.out.println(JSON.toJSONString(future)); future.get(); public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) { return andTree(cfs, 0, cfs.length - 1); } //This method is a recursive method. The bisection method executes a wait for two tasks, and each bisection creates a depency of CompletableFuture static CompletableFuture<Void> andTree(CompletableFuture<?>[] cfs, int lo, int hi) { CompletableFuture<Void> d = new CompletableFuture<Void>(); if (lo > hi) // empty d.result = NIL; else { CompletableFuture<?> a, b; int mid = (lo + hi) >>> 1; if ((a = (lo == mid ? cfs[lo] : andTree(cfs, lo, mid))) == null || (b = (lo == hi ? a : (hi == mid+1) ? cfs[hi] : andTree(cfs, mid+1, hi))) == null) throw new NullPointerException(); if (!d.biRelay(a, b)) {//a,b The two subtasks are not all completed, take this branch BiRelay<?,?> c = new BiRelay<>(d, a, b);//Create a Completion, a dependency, and two sources a.bipush(b, c);//Push c to the stack properties of a and b c.tryFire(SYNC);//BiRelay triggers the actual operation } } return d; } //According to the literal translation of the method name: whether both propagations have been completed; if either of the two tasks is not completed, return false, and only return true when all of them are completed. boolean biRelay(CompletableFuture<?> a, CompletableFuture<?> b) { Object r, s; Throwable x; if (a == null || (r = a.result) == null || b == null || (s = b.result) == null) return false; if (result == null) { if (r instanceof AltResult && (x = ((AltResult)r).ex) != null) completeThrowable(x, r); else if (s instanceof AltResult && (x = ((AltResult)s).ex) != null) completeThrowable(x, s); else completeNull(); } return true; }How to wait for multiple threads to end?
//There are comments on each step of this method, only the key parts are written here private Object waitingGet(boolean interruptible) { Signaller q = null; boolean queued = false; int spins = -1; Object r; while ((r = result) == null) { if (spins < 0) spins = (Runtime.getRuntime().availableProcessors() > 1) ? 1 << 8 : 0; // Use brief spin-wait on multiprocessors else if (spins > 0) { if (ThreadLocalRandom.nextSecondarySeed() >= 0) --spins; } else if (q == null) q = new Signaller(interruptible, 0L, 0L); else if (!queued) //Create a signal thread and push it to the stack property of Completable. When the thread finishes executing, it will execute CompletableFuture, which depends on Completion. //That is, the q of the current Signaller type, please see the implementation of the tryFire method of the signal in the source code below queued = tryPushStack(q); else if (interruptible && q.interruptControl < 0) { q.thread = null; cleanStack(); return null; } else if (q.thread != null && result == null) { try { //Call Signaller's block and isReleasable methods, when the result cannot be obtained, it will be blocked, see the specific code of the block below ForkJoinPool.managedBlock(q); } catch (InterruptedException ie) { q.interruptControl = -1; } } } if (q != null) { q.thread = null; if (q.interruptControl < 0) { if (interruptible) r = null; // report interruption else Thread.currentThread().interrupt(); } } postComplete(); return r; } //Signaller's block method public boolean block() { if (isReleasable()) return true; else if (deadline == 0L)//When this step is executed, the thread will block LockSupport.park(this); else if (nanos > 0L) LockSupport.parkNanos(this, nanos); return isReleasable(); } //The tryFire method of the Signaller class final CompletableFuture<?> tryFire(int ignore) { Thread w; // no need to atomically claim if ((w = thread) != null) { thread = null; //The most important method is to execute an unpark method on the current thread. This method will be called when the postComplete() method is executed after all task threads are executed. //Wake up the current thread blocked because the calculation result cannot be obtained LockSupport.unpark(w); } return null; }