Thoroughly understand Java's future model

Source: Internet
Author: User
Tags joins throwable

Reprinted from: http://www.cnblogs.com/cz123/p/7693064.html

First scene: If you suddenly want to cook, but there is no kitchenware, there is no ingredients. Online purchase of kitchen utensils is more convenient, food products to the supermarket to buy more assured.

Implementation analysis: During the delivery of kitchen utensils, we certainly will not idle, can go to the supermarket to buy food materials. So, in the main thread inside another sub-thread to buy kitchenware online.

However, the result of the child thread execution is to return the cookware, and the Run method does not return a value. So, this is the difficulty, need to think about it.

Analog Code 1:

Package Test;public class Commoncook {public static void main (string[] args) throws Interruptedexception {long        StartTime = System.currenttimemillis ();        The first step online shopping kitchen onlineshopping thread = new onlineshopping ();        Thread.Start ();  Thread.Join ();  Ensure that the kitchen utensils delivered to//second step to the supermarket to buy ingredients Thread.Sleep (2000);        Simulated purchase of ingredients time Shicai Shicai = new Shicai ();        System.out.println ("Second step: Food ingredients in place");        The third step is to cook the ingredients with kitchenware System.out.println ("Step three: Begin to show the culinary Arts");                Cook (Thread.chuju, Shicai);    SYSTEM.OUT.PRINTLN ("Total elapsed" + (System.currenttimemillis ()-StartTime) + "MS");        }//online shopping kitchen thread static class Onlineshopping extends thread {private Chuju Chuju;            @Override public void Run () {System.out.println ("First step: Order");            SYSTEM.OUT.PRINTLN ("First step: Waiting for delivery");  try {thread.sleep (5000);  Simulated delivery time} catch (Interruptedexception e) {e.printstacktrace ();          } System.out.println ("First step: Express delivery to");        Chuju = new Chuju ();        }}//Cooking ingredients with kitchenware static void Cook (Chuju Chuju, Shicai shicai) {}//kitchen class static class Chuju {} Food material static Class Shicai {}} 

Operation Result:

The first step: the first step: waiting for delivery first step: Courier delivery to the second step: the ingredients in place the third step: start to show the total cooking time 7013ms

As you can see, multithreading has lost its meaning. We can't do anything until the kitchen is delivered. The corresponding code is to call the Join method to block the main thread.

Someone asked, do not block the main thread line???

No way!!!

From the code point of view, the Run method does not finish, the property Chuju is not assigned, or null. In other words, no kitchen utensils, how to cook.

Java now multithreading mechanism, the core method run is no return value, if you want to save the Run method inside the calculation results, you must wait for the Run method to calculate, no matter how time-consuming the calculation process.

Faced with this awkward situation, the programmer will think: During the calculation of the child thread Run method, can continue to execute asynchronously in the main thread???

Where There is a will,there is a way!!!

The core of this idea is the future model, the following first application of Java's own implementation of the future mode.

Analog Code 2:

Package Test;import Java.util.concurrent.callable;import Java.util.concurrent.executionexception;import Java.util.concurrent.futuretask;public class Futurecook {public static void main (string[] args) throws Interruptedexce        Ption, executionexception {Long startTime = System.currenttimemillis (); The first step online purchase kitchen utensils callable<chuju> onlineshopping = new callable<chuju> () {@Override PU                Blic Chuju Call () throws Exception {System.out.println ("First step: Order");                SYSTEM.OUT.PRINTLN ("First step: Waiting for delivery");  Thread.Sleep (5000);                Analog Delivery Time System.out.println ("First step: Express delivery to");            return new Chuju ();        }                    };        futuretask<chuju> task = new futuretask<chuju> (onlineshopping);        New Thread (Task). Start ();  The second step to the supermarket to buy ingredients Thread.Sleep (2000);        Simulated purchase of ingredients time Shicai Shicai = new Shicai ();        System.out.println ("Second step: Food ingredients in place"); Step three cooking with kitchen utensilsINGREDIENTS if (!task.isdone ()) {//Contact courier, ask whether the arrival System.out.println ("The third step: the kitchen is not yet, the mood is good to wait (bad mood on the call cancel method cancel order)");        } Chuju Chuju = Task.get ();        SYSTEM.OUT.PRINTLN ("Step three: Kitchen utensils in place, beginning to show the culinary Arts");                Cook (Chuju, Shicai);    SYSTEM.OUT.PRINTLN ("Total elapsed" + (System.currenttimemillis ()-StartTime) + "MS"); }//Cooking ingredients with kitchenware static void Cook (Chuju Chuju, Shicai shicai) {}//kitchen class static class Chuju {}//Food Material static class Shicai {}}

Operation Result:

The first step: the first step: waiting for delivery second step: Food ingredients in place the third step: the kitchen is not yet, the mood is good to wait (the mood is bad call Cancel method cancels the order) first step: Express delivery to the third step: kitchenware in place, began to show the total time of cooking 5005ms

Can see, in the courier sent kitchen utensils, we are not idle, we can go to buy food, and we know that the kitchen is not even to the kitchen can not be in the time, cancel the order.

Good magic, there is no.

The following is a detailed analysis of the second paragraph of code:

1) the time-consuming online purchase of kitchenware logic, packaged into a callable call method inside.

Public interface Callable<v> {    /**     * Computes a result, or throws an exception if unable to does so.     *     * @return computed result     * @throws Exception if unable to compute a result     *    /V call () throws Excepti On;}

The callable interface can be seen as a complement to the Runnable interface, with the call method having a return value, and can throw an exception.

2) take the callable instance as a parameter, generate a Futuretask object, and then treat the object as a runnable, as an argument to another thread.

public class Futuretask<v> implements Runnablefuture<v>
Public interface runnablefuture<v> extends Runnable, future<v>
Public interface Future<v> {    Boolean cancel (Boolean mayinterruptifrunning);    Boolean iscancelled ();    Boolean isDone ();    V get () throws Interruptedexception, Executionexception;    V get (long timeout, timeunit unit)        throws Interruptedexception, Executionexception, TimeoutException;}

The core interface in this inheritance system is the future. The core idea of the future is: A method F, the calculation process can be very time consuming, waiting for F to return, obviously unwise. You can immediately return a future when you call F, and you can control the computational process of method F by using the data structure of the future.

The controls here include:

Get method: Gets the result of the calculation (it must wait if it is not finished)

Cancel method: You can cancel the calculation process before you have finished calculating

Isdone method: Determine if the calculation is complete

IsCancelled method: Determine if the calculation was canceled

The design of these interfaces is perfect, the implementation of Futuretask is doomed to be not simple, and later.

3) in the third step, call the Isdone method to view the state, and then directly call the Task.get method to get the kitchen utensils, but this is not delivered, so still will wait 3 seconds. Comparing the results of the first code execution, we saved 2 seconds. This is because during the courier delivery, we go to the supermarket to buy the ingredients, these two things are executed asynchronously during the same time period.

With the above 3 steps, we have completed the most basic application of Java native future mode. The following specific analysis of the implementation of Futuretask, first look at JDK8, and then compare the implementation of JDK6.

Since Futuretask is also a runnable, look at its Run method

public void Run () {if ' state! = NEW | | !            Unsafe.compareandswapobject (this, runneroffset, NULL, Thread.CurrentThread ()))        Return try {callable<v> c = callable;//The callable here is the IF (c! = null && state = = NE) from the construction method                W) {V result;                Boolean ran;                    try {result = C.call ();                ran = true;                    } catch (Throwable ex) {result = null;                    ran = false; SetException (ex); Save the exception that is thrown by the call method} if (ran) set (result);            Save the execution result of the call method}} finally {//runner must be non-null until state was settled to            Prevent concurrent calls to run () runner = null;       State must is re-read after nulling runner to prevent//leaked interrupts     int s = State;        if (s >= interrupting) handlepossiblecancellationinterrupt (s); }    }

First look at the logic inside the try statement block, and find that the main logic of the Run method is to run the callable call method, and then save the result or exception (using a property result). It is hard to imagine that the exception thrown by the call method is saved.

This represents the property of the state what the hell is that?

     * Possible State transitions:     * New, completing, NORMAL     * NEW-and completing-exceptional     * N EW-CANCELLED     * NEW-interrupting-interrupted     */    private volatile int state;    private static final int NEW          = 0;    private static final int completing   = 1;    private static final int NORMAL       = 2;    private static final int exceptional  = 3;    private static final int CANCELLED    = 4;    private static final int interrupting = 5;    private static final int interrupted  = 6;

Consider Futuretask as a future, then its function is to control the execution of callable call method, in the process of execution will naturally have a state of transformation:

1) A futuretask is newly created, State is new, and the competing and interrupting are used for instantaneous state, which is very short ( Why do you want to set up this state??? the normal representative successfully completed; Exceptional represents an exception to the execution process; Canceled represents the execution process is canceled; interrupted is interrupted.

2) Completion of the execution process: completing, NEW--

3) exception occurred during execution: completing, NEW---exceptional

4) The execution process is canceled: NEW--CANCELLED

5) thread interruption during execution: interrupting, NEW---interrupted

Please read the details in the code, such as state judgment and CAS operation.

And look at the implementation of the Get method:

    Public V get () throws Interruptedexception, executionexception {        int s = State;        if (s <= completing)            s = Awaitdone (false, 0L);        Return report (s);    }
    private int Awaitdone (Boolean timed, Long Nanos) throws Interruptedexception {final long deadline = Tim Ed?        System.nanotime () + nanos:0l;        Waitnode q = null;        Boolean queued = false; for (;;)                {if (thread.interrupted ()) {removewaiter (q);            throw new Interruptedexception ();            } int s = state;                if (S > Completing) {if (q! = null) Q.thread = NULL;            return s;            } else if (s = = completing)//cannot time out yet Thread.yield ();            else if (q = = null) Q = new Waitnode ();                                                     else if (!queued) queued = Unsafe.compareandswapobject (this, Waitersoffset,            Q.next = waiters, q);                else if (timed) {Nanos = Deadline-system.nanotime ();                if (Nanos <= 0L) {    Removewaiter (q);                return state;            } Locksupport.parknanos (this, Nanos);        } else Locksupport.park (this); }    }

The logic of the Get method is simple, if the execution of the call method is complete, the result is given out, and if it is not, the current thread is suspended for waiting. The logic of the dead loop inside the Awaitdone method can be understood several times; The main innovation that hangs threads in it is to define the Waitnode class to organize multiple waiting threads into queues, which is the biggest difference from JDK6 implementations.

When the suspended thread is awakened:

    private void Finishcompletion () {        //Assert state > completing;        for (Waitnode Q; (q = waiters) = null;) {            if (Unsafe.compareandswapobject (this, waitersoffset, q, NULL))} {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;            }        }        Done ();        callable = null;        To reduce footprint    }

The above is the general implementation of JDK8 logic, such as Cancel, set and other methods, please read the reader himself.

Let's look at the realization of JDK6.

The basic operation of JDK6 's futuretask is achieved through its own internal class sync, and sync inherits from Abstractqueuedsynchronizer, a very high-rate concurrency tool class

       /** state value representing this task is running */        private static final int running   = 1;        /** state value representing this task ran */        private static final int ran       = 2;        /** state value representing this task was cancelled */        private static final int cancelled = 4;        /** the underlying callable */        private final callable<v> callable;        /** the result to return from Get () */        private V result;        /** the exception to throw from Get () */        private throwable exception;

There are only a few basic states, and the results and exceptions are saved separately.

        V Innerget () throws Interruptedexception, executionexception {            acquiresharedinterruptibly (0);            if (getState () = = CANCELLED)                throw new Cancellationexception ();            if (Exception! = null)                throw new Executionexception (exception);            return result;        }

The way to handle waiting thread queues in this get method is to call the Acquiresharedinterruptibly method, and readers who have read my previous blog posts should be very familiar with it. Where the waiting thread queue, thread hangs and wake logic, here no longer repeat, if not understand, please go left.

Finally, take a look at the more advanced applications derived from the future model.

Another scenario: We write a simple database connection pool that can reuse database connections and work properly in high concurrency situations.

Implementation Code 1:

Package Test;import Java.util.concurrent.concurrenthashmap;public class ConnectionPool {    private Concurrenthashmap<string, connection> pool = new concurrenthashmap<string, connection> ();        Public Connection getconnection (String key) {        Connection conn = null;        if (Pool.containskey (key)) {            conn = Pool.get (key);        } else {            conn = createconnection ();            Pool.putifabsent (KEY, conn);        }        return conn;    }        Public Connection createconnection () {        return new Connection ();    }        Class Connection {}}

We use concurrenthashmap so that we don't have to set the Getconnection method to synchronized (which can also be used with lock), and when multiple threads invoke the Getconnection method at the same time, the performance increases dramatically.

It seems perfect, but it may lead to the creation of redundant connections, which can be deduced once again:

At some point, there are 3 threads entering the Getconnection method, the call Pool.containskey (key) returns false, and 3 threads each create a connection. Although the put method of Concurrenthashmap only joins one of them, it generates 2 extra connections. If it is a real database connection, it can cause great waste of resources.

Therefore, our current difficulty is: How to access the Getconnection method in multiple threads, only one createconnection is executed once.

Combining the implementation of the previous future model: When 3 threads are to create a connection, if only one thread executes the CreateConnection method to create a connection, the other 2 threads just need to use this connection. Extension, put the CreateConnection method into a callable call method, and then generate Futuretask. We just have to have one thread execute the Futuretask run method, and the other thread just executes the GET method.

On the code:

Package Test;import Java.util.concurrent.callable;import Java.util.concurrent.concurrenthashmap;import    Java.util.concurrent.executionexception;import Java.util.concurrent.futuretask;public class ConnectionPool { Private concurrenthashmap<string, futuretask<connection>> pool = new concurrenthashmap<string,    Futuretask<connection>> (); Public Connection getconnection (String key) throws Interruptedexception, executionexception {futuretask<connect        ion> connectiontask = Pool.get (key);        if (connectiontask! = null) {return connectiontask.get ();                } else {callable<connection> callable = new callable<connection> () {@Override                Public Connection Call () throws Exception {return createconnection ();            }            };            futuretask<connection> NewTask = new futuretask<connection> (callable); Connectiontask = Pool.putifabseNT (key, NewTask);                if (Connectiontask = = null) {connectiontask = NewTask;            Connectiontask.run ();        } return Connectiontask.get ();    }} public Connection createconnection () {return new Connection (); } class Connection {}}

Once again: When 3 threads enter the Else statement block at the same time, each creates a futuretask, but Concurrenthashmap only joins one. The first thread returns null after executing the Pool.putifabsent method, then Connectiontask is assigned, then executes the Run method to create the connection, and the last get. The subsequent thread execution Pool.putifabsent method does not return null, only the GET method is executed.

In a concurrent environment, with Futuretask as the intermediate conversion, the successful implementation of a method to be executed by only one thread.

It's so much, it's so painstaking!!! Ha ha

Thoroughly understand Java's future model

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.