CompletableFuture source code appreciation

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;
        }

  

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326334427&siteId=291194637