Future asynchronous execution

Posted May 26, 20205 min read

Foreword

With the development of business, the system has more and more functions. At this time, many business operations need to be performed asynchronously to improve efficiency. Most of the time, we can use the thread pool to achieve asynchronous execution requirements. However, sometimes the main process needs to wait for other tasks to complete before continuing execution, or the main process needs to know the results of other tasks. At this time, Need to adopt the merging of threads.

Before talking about thread merging, let's take a look at how to use Thread to realize the main thread is waiting for other threads.

join merge thread

When a thread needs to wait for other threads to finish execution before continuing execution, we can use join or CountDownLatch, here only to say join, CountDownLatch has been said in the previous article, will not repeat them here.

When thread A calls thread B's join method, thread A will block until thread B finishes executing.

Here is a simple example:how to go home from work, how to perform daily operations such as cooking, cooking and boiling water using multi-threading. The following is the specific process:

join merge thread

The main thread needs to wait for the cooking thread and the boiling water thread to complete before it can continue the following meal and drinking operations. Let's see how the code is implemented

public class JoinDemo {

    static class RiceThread extends Thread {
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread(). GetName() + "Start cooking");
                Thread.sleep(10 * 1000);
                System.out.println(Thread.currentThread(). GetName() + "The rice is cooked");
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    static class WaterThread extends Thread {
        @Override
        public void run() {
            try {
                System.out.println(Thread.currentThread(). GetName() + "Start boiling water");
                Thread.sleep(5 * 1000);
                System.out.println(Thread.currentThread(). GetName() + "Water is done");
            } catch(InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String []args) {
        try {
            System.out.println(Thread.currentThread(). GetName() + "go home");
            //Start the cooking thread
            RiceThread riceThread = new RiceThread();
            riceThread.start();
            //Start the boiling water thread
            WaterThread waterThread = new WaterThread();
            waterThread.start();
            System.out.println(Thread.currentThread(). GetName() + "Start cooking");
            Thread.sleep(3 * 1000);
            System.out.println(Thread.currentThread(). GetName() + "Dish is ready, waiting for dinner");
            waterThread.join();
            riceThread.join();
            System.out.println(Thread.currentThread(). GetName() + "Eat and drink ...");
        } catch(InterruptedException e) {
            e.printStackTrace();
        }

    }
}

//output
main home
Thread-0 starts cooking
main start cooking
Thread-1 starts to boil water
The main dish is ready, waiting to eat
Thread-1 water boiled
Thread-0 rice is cooked
Main meal and drink water. . .

The above example shows that the use of join can realize the main thread waits for other threads to complete before executing down, but there is a problem here, what to do if there is a problem with the execution of other threads, the main thread is not known at this time, because the run in Thread The method has no return value. Is there any way to do it asynchronously and get the result? Can be solved with Future.

Future

Future is an interface, it has the following methods

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 execution result of the asynchronous task is mainly obtained through the get() method inside. This method is blocked until the execution of the asynchronous task is completed.

Future is just an interface, we can't use it directly, we need its implementation class to complete, at this time we don't need to implement this class by ourselves, Java has improved a ready-made implementation class-FutureTask.

Let's take a look at the construction method of FutureTask

public FutureTask(Callable <V> callable) {
    if(callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;
}

Constructing FutureTask needs to pass in a Callable object, so what is this Callable?

We all know that a new thread can be implemented by implementing the Runable interface. This Callable is similar to the Runable interface, and it has only one method:

@FunctionalInterface
public interface Callable <V> {
    V call() throws Exception;
}

But the biggest difference between it and Runable is that this method has a return value. At this point, everyone should know that the asynchronous execution result is obtained through this return value.

The last question remains, the only thread in Java is the Thread class, so how does this Callable implement to start a new thread? This requires the use of the FutureTask mentioned above. This class implements the RunnableFuture interface, and this interface inherits both the Runable and Future interfaces. Runable can be passed into the Thread class as a parameter, which connects Callable and Thread . With that said, everyone may be a little confused. Let s rewrite the above cooking process with code.

public class FutureDemo {

    static class RiceThread implements Callable <Boolean> {

        @Override
        public Boolean call() throws Exception {
            try {
                System.out.println(Thread.currentThread(). GetName() + "Start cooking");
                Thread.sleep(10 * 1000);
                //Problems with simulated cooking
                return false;
//System.out.println(Thread.currentThread(). GetName() + "The rice is cooked");
//return true;
            } catch(InterruptedException e) {
                System.out.println("An exception occurred in cooking");
                return false;
            }
        }
    }

    static class WaterThread implements Callable <Boolean> {
        @Override
        public Boolean call() throws Exception {
            try {
                System.out.println(Thread.currentThread(). GetName() + "Start boiling water");
                Thread.sleep(5 * 1000);
                System.out.println(Thread.currentThread(). GetName() + "Water is done");
                return true;
            } catch(InterruptedException e) {
                System.out.println("Abnormal water boiling");
                return false;
            }
        }
    }

    public static void main(String []args) {
        try {
            Callable <Boolean> riceThread = new RiceThread();
            FutureTask <Boolean> rickTask = new FutureTask <>(riceThread);
            Thread rThread = new Thread(rickTask);

            Callable <Boolean> waterThread = new WaterThread();
            FutureTask <Boolean> waterTask = new FutureTask <>(waterThread);
            Thread wThread = new Thread(waterTask);
            System.out.println(Thread.currentThread(). GetName() + "go home");
            //Start the cooking thread
            rThread.start();
            //Start the boiling water thread
            wThread.start();
            System.out.println(Thread.currentThread(). GetName() + "Start cooking");
            Thread.sleep(3 * 1000);
            System.out.println(Thread.currentThread(). GetName() + "Dish is ready, waiting for dinner");
            Boolean wFlag = waterTask.get();
            if(! wFlag) {
                System.out.println(Thread.currentThread(). GetName() + "There is a problem with boiling water, there is no water to drink");
                return;
            }
            Boolean rFlag = rickTask.get();
            if(! rFlag) {
                System.out.println(Thread.currentThread(). GetName() + "There is a problem with cooking, no food to eat");
                return;
            }
            System.out.println(Thread.currentThread(). GetName() + "Eat and drink ...");
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

}

//output
main home
main start cooking
Thread-0 starts cooking
Thread-1 starts to boil water
The main dish is ready, waiting to eat
Thread-1 water boiled
There is something wrong with main cooking, no food

The above simulates the return of data from other threads. The get() method of the main thread's FutureTask gets these data, and then performs the corresponding logic.

to sum up

In Java, threads waiting for other threads to finish execution can be implemented using join; threads waiting for other threads and needing to obtain results can be implemented using FutureTask. But the join() and the get() method in FutureTask are both blocked. Is there any way to accomplish the above requirements without blocking? I'll talk about it next time!