CompletableFuture of JUC Concurrent Programming

Future

future is a new interface added by java5, which provides a function of asynchronous parallel computing

The interface defines some methods to operate the execution of asynchronous tasks, such as obtaining the execution results of asynchronous tasks, canceling the execution of tasks, judging whether the task is canceled, and judging whether the task is completed

Purpose: Asynchronous multi-thread execution with return results, features: multi-thread/return/asynchronous task

Supplement: Runnable implements the run method, which has no return value and no exception. Callable implements the call method, which has a return value and needs to handle exceptions

The Future interface is commonly used to implement the class Future Task asynchronous task

Code

public class CompletableFutureDemo {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new MyThread());
        Thread t1 = new Thread(futureTask,"t1");
        t1.start();
        System.out.println(futureTask.get());
    }
}



class MyThread implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("----come in call()");
        return "hello Callable";
    }
}

 

Advantages and disadvantages of future

NO: 1. The get method is easy to block: once called, if the calculation is not completed, the program is easy to block

2. isDone polling: The polling method will consume CPU resources, and it may not be possible to get the calculation results in time

Summary: future is not very friendly to the acquisition of results, and the task results can only be obtained by blocking or polling

Note: The polling method in the actual program is not better than blocking. This is for the user to read. It is impossible for the user to see your program and stop there. Writing this way can give a reminder, and it will not be directly abnormal.

CompletableFuture's asynchronous optimization idea

Background: It is completely OK to use Future for simple business scenarios, but for complex business scenarios, such as:

1. Callback notification: To deal with the completion time of the Future, you can tell me when it is completed, that is, our callback notification, to judge whether the task is completed by polling, which is very CPU-intensive and the code is not elegant

2. Create asynchronous tasks: Future+thread pool cooperation

3. The front and rear dependencies of multiple tasks can be combined and processed: if you want to combine the calculation results of multiple asynchronous tasks, the calculation results of the latter asynchronous task need the value of the result of the previous asynchronous task, and combine two or more asynchronous calculations into one asynchronous Computation, these asynchronous calculations are independent of each other, while the latter one depends on the result of the previous processing

4. The fastest calculation speed: when a task in the Future collection ends the fastest, return the result, and return the first processing result

CompletableFuture provides a mechanism similar to the observer pattern, which allows the listening party to be notified after the task is completed

class schema description

 

Interface CompletionStage

1. CompletionStage represents a certain stage in the asynchronous computing process. After one stage is completed, another stage may be triggered

2. The execution of a stage can be a Function, Consumer or Runnable

3. The execution of a stage may be triggered by the completion of a single stage, or it may be triggered by multiple stages together

Represents a certain stage in the asynchronous computing process. After one stage is completed, another stage may be triggered

class CompletableFuture

1. In java8, CompletableFuture provides a very powerful extension function of Future, which can help us simplify the complexity of asynchronous programming, and provides the ability of functional programming, which can process calculation results through callback, and also provides conversion and Combining methods of ComplttableFuture

2. It may represent a clearly completed Future, or it may represent a completion stage, which supports triggering some functions or performing an action after the calculation is completed

3. It implements the Future and CompletionStage interfaces

Note: CompletableFuture has been introduced since java8, which is an enhanced version of Future, which reduces blocking and polling, and can pass in a callback object. When the asynchronous task is completed or an exception occurs, the callback method of the callback object is automatically called

advantage:

1. When the asynchronous task ends, it will automatically call back the method of an object

2. After the main thread sets the callback, it no longer cares about the execution of asynchronous tasks, and the asynchronous tasks can be executed sequentially

3. When an asynchronous task fails, it will automatically call back the method of an object

Functional Interface Review 

 Common methods of CompletableFuture

1. Obtain results and trigger calculations

public class CompletableFutureAPIDemo {
    public static void main(String[] args) {
        CompletableFuture<String> completableFuture = CompletableFuture.supplyAsync(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "abc";
        });

        //System.out.println(completableFuture.get());   阻塞主线程获取结果
        //System.out.println(completableFuture.get(2L,TimeUnit.SECONDS)); 等待指定时间获取结果
        //System.out.println(completableFuture.join());  阻塞线程获取结果
        System.out.println(completableFuture.getNow("xxx")); //立即获取结果不阻塞,如果没计算完成返回XXX
        System.out.println(completableFuture.complete("completeValue")+"\t"+completableFuture.join());
        //是否打断get方法立刻获得括号值
    }
}

2. Process the calculation results

public class CompletableFutureAPI2Demo {
    public static void main(String[] args) {

        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        //thenApply
        //计算结果存在依赖关系,这两个线程串行化
        //异常相关:由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停
        //handle:区别:有异常也可以往下一步走,根据带的异常参数可以进一步处理
        CompletableFuture.supplyAsync(() -> {
            try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
            System.out.println("111");
            return 1;
        },threadPool).handle((f,e) -> {
            int i = 10/0;
            System.out.println("222");
            return f+2;
        }).thenApply(f -> {
            System.out.println("333");
            return f+3;
        }).whenComplete((v,e) -> {
            if (e == null){
                System.out.println("------计算结果:"+v);
            }
        }).exceptionally(e -> {
            e.printStackTrace();
            System.out.println("e = " + e.getMessage());
            return null;
        });
        threadPool.shutdown();
        System.out.println(Thread.currentThread().getName()+"-----主线程先去忙其他任务");
    }
}

3. Consume the calculation results

//接受任务的处理结果,并消费处理,无返回结果
public class CompletableFutureAPI3Demo {
    public static void main(String[] args) {
//        CompletableFuture.supplyAsync(() -> {
//            return 1;
//        }).thenApply(f -> {
//            return f+2;
//        }).thenApply(f -> {
//            return f+3;
//        }).thenAccept(System.out::println);

        //任务之间的顺序执行
        /**
         * 1、thenRun:任务A执行完执行任务B,并且B不需要A的结果
         * 2、thenAccept:任务  A执行完执行B,B需要A的结果,但是任务B无返回值
         * 3、thenApply:任务A执行完执行B,B需要A的结果,同时任务B有返回值
         */

        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(r-> System.out.println("r = " + r)).join());
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(r -> r+"ResuleB").join());

    }
}

4. Choose the calculation speed

public class CompletableFutureFastDemo {
    public static void main(String[] args) {
        CompletableFuture<String> playA = CompletableFuture.supplyAsync(() -> {
            System.out.println("A come in");
            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
            return "playA";
        });

        CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {
            System.out.println("B come in");
            try {TimeUnit.SECONDS.sleep(3);} catch (InterruptedException e) {e.printStackTrace();}
            return "playB";
        });
        CompletableFuture<String> result = playA.applyToEither(playB, f -> f  + " is winer");
        System.out.println(Thread.currentThread().getName()+"\t"+"--------"+result.join());
    }
}

5. Combine the calculation results

public class CompletableFutureCombineDemo {
    public static void main(String[] args) {
        CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t ---启动");
            try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}
            return 10;
        });

        CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t ---启动");
            try {TimeUnit.SECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}
            return 20;
        });

        CompletableFuture<Integer> result = completableFuture1.thenCombine(completableFuture2, (x, y) -> {
            System.out.println("开始两个结果合并:" + x + y);
            return x + y;
        });
        System.out.println("result.join() = " + result.join());
    }
}

Note: CompletableFuture thread pool running selection

1. If no custom thread pool is passed in, the default thread pool ForkJoinPool is used

2. A custom thread pool is passed in

If you pass in a custom thread pool when you execute the first task

When calling the thenRun method to execute the second task, the second task and the first task use the same thread pool

When calling the thenRunAsync method to execute the second task, the first task uses the thread pool passed in by yourself, and the second task uses the ForkJoin thread pool

Remark:

It is possible to process too fast, the system optimizes the switching principle, and directly uses the main thread to process

public class CompletableFutureWithThreadPoolDemo {
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(5);

        try {
            CompletableFuture<Void> completableFuture = CompletableFuture.supplyAsync(() -> {
                try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println("1号任务" + "\t" + Thread.currentThread().getName());
                return "abcd";
            },threadPool).thenRunAsync(() -> {
                try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println("2号任务" + "\t" + Thread.currentThread().getName());
            }).thenRun(() -> {
                try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println("3号任务" + "\t" + Thread.currentThread().getName());
            }).thenRun(() -> {
                try {TimeUnit.MILLISECONDS.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}
                System.out.println("4号任务" + "\t" + Thread.currentThread().getName());
            });
            System.out.println(completableFuture.get(2L, TimeUnit.SECONDS));
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            threadPool.shutdown();
        }
    }
}

Guess you like

Origin blog.csdn.net/weixin_45934981/article/details/130214202