Multithreading and high concurrent programming -- Future source code analysis

1, Concept

A result of Future calculation. Provides methods to check if the calculation is complete, wait for it to complete, and retrieve the calculation results. The result can only be retrieved after the calculation is complete using the method get, blocking if necessary until it is ready. Cancellation is performed by the cancel method. Provides other methods to determine whether the task is completed or cancelled. After the calculation is completed, the calculation cannot be cancelled. If you want to use Future in order to be irreversible, but do not provide available results, you can declare the type of Future <? > table and return null as the result of the underlying task.  

public interface Future<V> {
    //Attempt to cancel this task. If the task has completed, has been cancelled, or cannot be cancelled for some other reason, the attempt will fail.
    //If successful, and cancel The task will never run if it has not yet started.
    //If the task has already started, then mayInterruptIfRunning Parameter determines whether the thread executing the task should be interrupted to attempt to stop the task.
    //mayInterruptIfRunning == true, Indicates that the thread in execution is interrupted, false Indicates to let the thread complete normally
    boolean cancel(boolean mayInterruptIfRunning);
    //If this task is cancelled before normal completion, return to true. 
    boolean isCancelled();
    //If this task is complete, return to true. Completion may be caused by a normal termination, exception, or cancellation, in which case this method will return true. 
    boolean isDone();
    //Wait for the calculation to complete if necessary, and then retrieve its results
    V get() throws InterruptedException, ExecutionException;
    //Wait a maximum of a given time to complete the calculation if necessary, and then retrieve its results, if any.
    V get(long timeout, TimeUnit unit)
        throws InterruptedException, ExecutionException, TimeoutException;
}

  

Future is an interface that provides methods to detect whether the current task has ended or not. You can also wait for the task to end and get a result. By calling get() method of future, you can return a result value when the task is finished. If the work is not finished, the current thread will be blocked until the task is finished. You can stop one by calling cancel() method If the task has been stopped, the cancel() method will return true; if the task has been completed or stopped or the task cannot be stopped, the cancel() method will return false. When a task is successfully stopped, it cannot be performed again. The isDone() and isCancel() methods determine whether the current work is complete and cancelled.   

Class diagram structure:

 

  • ScheduledFuture: this interface indicates that a delayed behavior can be cancelled. Usually a scheduled future is the result of the scheduled executorservice task;
  • RunnableFuture: this interface inherits both the Future interface and the Runnable interface. After the run() method is executed successfully, the execution result can be accessed through Future;
  • ForkJoinTask: an abstract task-based class that can be executed through the ForkJoinPool. A ForkJoinTask is similar to a thread entity, but is lightweight relative to a thread entity. A large number of tasks and subtasks will be hung up by the real threads in the ForkJoinPool pool at the cost of some usage restrictions;
  • Completabilefuture: a Future class is a display completion, and can be used as a completion level to trigger supported dependent functions and behaviors through its completion. When two or more threads want to complete or cancel operations, only one can succeed;
  • RunnableScheduledFuture: perform delayed and periodic tasks; after successfully executing the run() method, you can access the execution results through Future;
  • FutureTask: cancelable asynchronous computing
    • This class provides a basic implementation of Future, with the methods of starting and canceling calculation, querying whether the calculation is complete, and retrieving the calculation results. Results can only be retrieved after the calculation has completed; if the calculation has not completed, the get method blocks. Once the calculation is complete, it cannot be restarted or cancelled (unless the calculation is called using runAndReset());
    • A FutureTask can be used to wrap Callable or Runnable objects. Because FutureTask implements Runnable, a FutureTask can be submitted to an Executor for execution;
  • Recursive task: recursive result ForkJoinTask;
  • Recursive action: recursive result ForkJoinTask;

2, Usage

A scene, we need to learn to cook, then we need to prepare Kitchenware and ingredients, kitchenware through e-commerce online shopping, ingredients to the vegetable market selection. Then we can use multithreading to do it concurrently. That is to say, we can order online first, and go to the food market to buy food materials during the period of waiting for delivery from the courier, so as to save time and improve efficiency.

  1. Start the Thread directly, and use the class inheritance Thread rewrite method to realize online shopping. The join block does not start cooking until the kitchen utensils arrive.
    public class FutureTest {
        public static void main(String[] args) throws InterruptedException {
            long startTime = System.currentTimeMillis();
            OnlineShopping shopping = new OnlineShopping();
            shopping.start();
            Thread.sleep(2000); / / waiting for delivery to finish
            System.out.println("step 2: ingredients in place");
            shopping.join(); / / block the order until it is delivered by express delivery
            System.out.println("step 3: start cooking");
            System.out.println("total time:" + (System.currentTimeMillis() - startTime) + "ms");
        }
    
        static class OnlineShopping extends Thread {
            @Override
            public void run() {
                System.out.println("step 1: place an order");
                System.out.println("step 1: waiting for delivery");
                try {
                    Thread.sleep(5000); / / delivery in progress
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("first step: express delivery");
            }
        }
    }
    //======Results======
    Step 1: place an order
     Step 1: waiting for delivery
     Step 2: food materials in place
     Step 1: express delivery
     Step 3: start cooking
     Total time: 5003ms
  2. Using Future mode to complete the above operations, and using Callable to return results to obtain kitchenware, you can flexibly operate the order through FutureTask, which shows that Future mode is more flexible than inheriting the order completed by Thread,
    public class FutureTest {
        public static void main(String[] args) throws Exception {
            long startTime = System.currentTimeMillis();
            Callable<String> shopping = () ->{
                System.out.println("step 1: place an order");
                System.out.println("step 1: waiting for delivery");
                Thread.sleep(5000); / / courier delivering
                System.out.println("first step: express delivery");
                return "kitchenware arrived";
            };
            FutureTask<String> task = new FutureTask<>(shopping);
            new Thread(task).start();
            Thread.sleep(2000); / / ensure that the order is placed in "waiting for delivery"
            System.out.println("step 2: ingredients in place");
            if (!task.isDone()) {/ / contact the courier and ask if the goods arrive
                System.out.println("step 3: wait in a good mood before the kitchen utensils arrive (call cancel method to cancel the order in a bad mood)");
            }
            String chuju = task.get(); / / get kitchenware
            System.out.println("step 3: start cooking");
            System.out.println("total time:" + (System.currentTimeMillis() - startTime) + "ms");
        }
    }
    //======Results======
    Step 1: place an order
     Step 1: waiting for delivery
     Step 2: food materials in place
     Step 3: wait for the kitchen utensils to arrive before they are in a good mood (call cancel method to cancel the order if they are in a bad mood)
    Step 1: express delivery
     Step 3: start cooking
     Total time: 5048ms

3, Analysis

Trilogy using Future mode:

  1. Create Callable override call method, encapsulate online shopping logic into call, and return definition result "kitchen utensils";
    public interface Callable<V> {
        V call() throws Exception;
    }
  2. Create FutureTask and put the Callable instance into the construction method of FutureTask;
    public class FutureTask<V> implements RunnableFuture<V>{
        public FutureTask(Callable<V> callable) {
            if (callable == null)
                throw new NullPointerException();
            this.callable = callable;
            this.state = NEW;       // ensure callable Visibility of
        }
        public FutureTask(Runnable runnable, V result) {
            this.callable = Executors.callable(runnable, result);
            this.state = NEW;       // ensure callable Visibility of
        }
    }
    public interface RunnableFuture<V> extends Runnable, Future<V> {
        void run();
    }

    The run method of FutureTask: the call of Callable is called by the run method of FutureTask, not run asynchronously;

    public void run() {
            // 1. If state !=  NEW Explain run Method has been run, direct return
            // 2. If state == NEW && CAS Competitive settings runner Failed, indicating that there are other threads running, direct return
            // NEW The state of is initialized by the constructor, runner Yes run this Callable Thread of
            if (state != NEW ||
                !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                             null, Thread.currentThread()))
                return;
            try {
                Callable<V> c = callable;// there callable It's from the construction method
                if (c != null && state == NEW) {
                    V result;
                    boolean ran;// identifier 
                    try {
                        result = c.call();//Get results
                        ran = true;
                    } catch (Throwable ex) {//abnormal
                        result = null;
                        ran = false;
                        setException(ex);
                    }
                    if (ran)//Success without exception, set return value
                        set(result);
                }
            } finally {
                // runner must be non-null until state is settled to
                // prevent concurrent calls to run()
                //Before the status is set, runner Must be non empty to prevent run()Concurrent calls to
                runner = null;
                // state must be re-read after nulling runner to prevent
                // leaked interrupts
                //In order to prevent leakage interruption, it must be empty runner Then set the status to repeat reading
                int s = state;
                // If final status >= INTERRUPTING,Processing interrupt
                // cancel Method will pass the parameter mayInterruptIfRunning To set state Value
                if (s >= INTERRUPTING)
                    handlePossibleCancellationInterrupt(s);
            }
        }

    State property state:

    private volatile int state;//Status, volatile Make states visible
        private static final int NEW          = 0;//State when construction method is created
        private static final int COMPLETING   = 1;//This is an intermediate state, which is used when it is completed and when an exception occurs
        private static final int NORMAL       = 2;//Final state at completion of run time
        private static final int EXCEPTIONAL  = 3;//Final state in case of exception
        private static final int CANCELLED    = 4;//Cancelled
        private static final int INTERRUPTING = 5;//Interrupting
        private static final int INTERRUPTED  = 6;//Interrupted
        
        //Possible state transformations:
        NEW -> COMPLETING -> NORMAL
        NEW -> COMPLETING -> EXCEPTIONAL
        NEW -> CANCELLED
        NEW -> INTERRUPTING -> INTERRUPTED

    Set set return value:

    private Object outcome;//adopt get Method
        //Set return value, status NEW -> COMPLETING -> NORMAL
        protected void set(V v) {
             // Why is it used here CAS Because it might cancel Methods generate competition.
            // If the competition fails, the cancellation of the competition is successful cancel Method, so skip it.
            if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {//NEW -> COMPLETING
                // Competitive success
                outcome = v;//outcome Return results for
                UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // The final status is NORMAL,COMPLETING -> NORMAL
                finishCompletion();
            }
        }

    setException runtime exception:

    //Status: NEW -> COMPLETING -> EXCEPTIONAL
    protected void setException(Throwable t) {
            // Why is it used here CAS Because it might cancel Methods generate competition.
            // If the competition fails, the cancellation of the competition is successful cancel Method, so skip it.
            if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {//NEW -> COMPLETING
                // Competitive success
                outcome = t; // outcome For a Throwable
                // Change the final status to EXCEPTIONAL
                UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state,COMPLETING -> EXCEPTIONAL
                finishCompletion();
            }
        }

    finishCompletion:

    //Delete the current thread and wake up all waiting threads, call done(),And cancel the method in progress
        private void finishCompletion() {
            // assert state > COMPLETING;
            //from waiters Start traversal at the end, for Spin until CAS success.
            for (WaitNode q; (q = waiters) != null;) {
                // Use CAS hold waiters Set to null,and awaitDone and removeWatier Method competition
                if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
                    // Spin wake all threads
                    for (;;) {
                        Thread t = q.thread;
                        if (t != null) {
                            q.thread = null;
                            LockSupport.unpark(t);// Wake up thread
                        }
                        WaitNode next = q.next;
                        if (next == null)
                            break;
                        q.next = null; // unlink to help gc
                        q = next;
                    }
                    break;
                }
            }
            // This empty method is mainly used for inheritance and rewriting, which is also the embodiment of template pattern
            done();
            callable = null;        // to reduce footprint
        }
        protected void done() { }
    
    
        //===========Example==============
        //ExecutorCompletionService Its function is to put the execution result of thread pool into a completed queue, which is convenient to obtain the execution result. The internal main function is to FutureTask Implementation class of QueueingFuture To achieve this function:
        private class QueueingFuture extends FutureTask<Void> {
                QueueingFuture(RunnableFuture<V> task) {
                    super(task, null);
                    this.task = task;
                }
                protected void done() { completionQueue.add(task); }//done The way is FutureTask Method. FutureTask Execute on completion done Method task Put in completed queue completionQueue. 
                private final Future<V> task;
            }

    get gets the return result:

    public V get() throws InterruptedException, ExecutionException {
            int s = state;//Get state
            if (s <= COMPLETING)//If the status is not completed, put the thread getting the result into the waiting list, and then block it until it is interrupted, completed or abnormal.
                s = awaitDone(false, 0L);
            return report(s);//Return results
        }
        private int awaitDone(boolean timed, long nanos)
            throws InterruptedException {
            final long deadline = timed ? System.nanoTime() + nanos : 0L;//For timing
            WaitNode q = null;
            boolean queued = false;
            for (;;) {//spin
                //If it has been interrupted, then removeWaiter,Throw interrupt exception
                if (Thread.interrupted()) {
                    removeWaiter(q);
                    throw new InterruptedException();
                }
                int s = state;
                if (s > COMPLETING) {//task It's already over.
                    if (q != null)
                        q.thread = null;
                    return s;
                }
                //It's coming to an end, get out of the way cpu
                else if (s == COMPLETING) // cannot time out yet
                    Thread.yield();
                // initialization WaitNode
                else if (q == null)
                    q = new WaitNode();
                 // Have you joined the team or not WaitNode To end
                else if (!queued)
                    // and finishCompletion and removeWaiter compete
                    // 1. finishCompletion Competitive success, description state Already > COMPLETING Then the next cycle will exit
                    // 2. removeWaiter Competitive success, description waiters It's changed. The next cycle is competing again
                    queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                         q.next = waiters, q);
                // If timing is used, judge whether it is timeout, and if it is, remove it WaitNode And return immediately without waiting for the result, otherwise blocking nanos
                else if (timed) {
                    nanos = deadline - System.nanoTime();
                    if (nanos <= 0L) {
                        removeWaiter(q);
                        return state;
                    }
                    LockSupport.parkNanos(this, nanos);
                }
                else
                    //Block until awakened (normal completion || abnormal || Interrupt)
                    LockSupport.park(this);
            }
        }
        //according to awaitDone Return status return result or throw exception
        private V report(int s) throws ExecutionException {
            Object x = outcome;
            if (s == NORMAL)//normal
                return (V)x;
            if (s >= CANCELLED)//cancel
                throw new CancellationException();
            // task Exception during execution
            throw new ExecutionException((Throwable)x);
        }

    removeWaiter:

    /**
             * Tries to unlink a timed-out or interrupted wait node to avoid
             * accumulating garbage.  Internal nodes are simply unspliced
             * without CAS since it is harmless if they are traversed anyway
             * by releasers.  To avoid effects of unsplicing from already
             * removed nodes, the list is retraversed in case of an apparent
             * race.  This is slow when there are a lot of nodes, but we don't
             * expect lists to be long enough to outweigh higher-overhead
             * schemes.
             *Try unlinking a timeout or broken wait node to avoid garbage. There is no CAS for the splicing of internal nodes,
             *Because this has no effect on the traversal of the releaser. In order to avoid the influence of the deleted nodes not spliced,
             *If there is an obvious competition, the list is traversed again. It's slow when there are many nodes, but we don't
             *The expectation list is long enough to offset the higher overhead plan.
             */
        private void removeWaiter(WaitNode node) {
            if (node != null) {
                node.thread = null;
                retry:
                for (;;) {          // restart on removeWaiter race
                    //Traverse the entire list
                    for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
                        s = q.next;
                        //hold q As the previous node, traverse the next node
                        if (q.thread != null)
                            pred = q;
                         // q.thread == null && pred != null,Indicates that the current node is not the first node, but an intermediate node
                         // Not used here CAS,If multiple threads traverse at the same time, the previous node becomes null,Then traverse from the beginning again
                         // Why not use CAS Because the author's idea is that this list will not be too long, so we should not make this list too long when we use it
                         // Action: connect the next node to the back of the previous node
                        else if (pred != null) {
                            pred.next = s;//hold s connection to pred behind
                            if (pred.thread == null) // check for race
                                continue retry;
                        }
                        // q.thread == null && pred == null,For the first node thread == null,
                        // Use here CAS,Because there may be multiple threads operating
                        // Operation: set the next node as the end node, and traverse from the beginning again if the competition fails
                        else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
                                                              q, s))
                            continue retry;
                    }
                    break;
                }
            }
        }

     isDone:

    public boolean isDone() {
            return state != NEW;
        }
  3. Create a Thread, put the FutureTask instance into the constructor, and start the Thread

Reference: https://www.jianshu.com/p/414cc2f0002c

Posted on Wed, 06 May 2020 03:14:55 -0400 by tymlls05