第二章:CompletableFuture

Future接口理论知识复习

Future接口(FutureTask实现类)定义了操作异步任务执行一些方法,如获取异步任务的执行结果、取消任务的执行、判断任务是否被取消、判断任务执行是否完毕等。

比如主线程让一个子线程去执行任务,子线程可能比较耗时,启动子线程开始执行任务后,主线程就去做其他事情了,忙其他事情或者先执行完,过了一会才去获取子任务的执行结果或变更的任务状态。

一句话:Future接口可以为主线程开一个分支任务,专门为主线程处理耗时和费力的复杂业务。

Future接口常用实现类FutureTask异步任务 

Future接口能干什么?

Future是Java5新加的一个接口,它提供了一种异步并行计算的功能。如果主线程需要执行一个很耗时的计算任务,我们就可以通过Future把这个任务放到异步线程中执行。 主线程继续处理其他任务或者先行结束,再通过Future获取计算结果。

代码说话:

  1. Runnable接口
  2. Callable接口
  3. Future接口和FutureTask实现类

目的:异步多线程任务执行且返回有结果,三个特点:多线程/有返回/异步任务。

本源的Future接口相关的架构

第二章:CompletableFuture_第1张图片

package com.lzx.juc.cf;

import java.util.concurrent.*;

/**
 * @author admin
 */
public class CompletableFutureDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask futureTask = new FutureTask<>(new MyThread());

        Thread t1 = new Thread(futureTask, "t1");
        t1.start();

        System.out.println(futureTask.get());
    }
    
}

class MyThread implements Callable {

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

}

Future编码实战和有缺点分析

优点

Future+线程池异步多线程任务配合,能显著提高程序的执行效率。

上述案例case

package com.lzx.juc.cf;

import java.util.concurrent.*;

/**
 * @author admin
 */
public class FutureThreadPoolDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //3个任务,目前开启多个异步任务线程来处理,请问耗时多少?
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        long startTime = System.currentTimeMillis();

        FutureTask futureTask1 = new FutureTask<>(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task1 over";
        });

        threadPool.submit(futureTask1);

        FutureTask futureTask2 = new FutureTask<>(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task2 over";
        });

        threadPool.submit(futureTask2);

        System.out.println(futureTask1.get());
        System.out.println(futureTask2.get());

        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒");


        System.out.println(Thread.currentThread().getName() + "\t -----end");
        threadPool.shutdown();
    }

    private static void m1() {
        //3个任务,目前只有一个线程main来处理,请问耗时多少?
        long startTime = System.currentTimeMillis();

        //暂停毫秒
        try {
            TimeUnit.MILLISECONDS.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        try {
            TimeUnit.MILLISECONDS.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("----costTime: " + (endTime - startTime) + " 毫秒");

        System.out.println(Thread.currentThread().getName() + "\t -----end");
    }
}

缺点

Code1

package com.bilibili.juc.cf;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 1 get容易导致阻塞,一般建议放在程序后面,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,容易程序堵塞。
 * 2 假如我不愿意等待很长时间,我希望过时不候,可以自动离开.
 *
 * @author admin
 */
public class FutureAPIDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        FutureTask futureTask = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() + "\t -----come in");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task over";
        });

        Thread t1 = new Thread(futureTask, "t1");
        t1.start();

        System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");

        //System.out.println(futureTask.get());
        System.out.println(futureTask.get(3,TimeUnit.SECONDS));
    }
}

get()阻塞

一旦调用get()方法求结果,如果计算没有完成容易导致程序阻塞。

Code2

package com.lzx.juc.cf;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

/**
 * 1 get容易导致阻塞,一般建议放在程序后面,一旦调用不见不散,非要等到结果才会离开,不管你是否计算完成,容易程序堵塞。
 * 2 假如我不愿意等待很长时间,我希望过时不候,可以自动离开.
 *
 * @author admin
 */
public class FutureAPIDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        FutureTask futureTask = new FutureTask<>(() -> {
            System.out.println(Thread.currentThread().getName() + "\t -----come in");
            try {
                TimeUnit.SECONDS.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task over";
        });

        Thread t1 = new Thread(futureTask, "t1");
        t1.start();

        System.out.println(Thread.currentThread().getName() + "\t ----忙其它任务了");

        //System.out.println(futureTask.get());
        //System.out.println(futureTask.get(3,TimeUnit.SECONDS));

        while (true) {
            if (futureTask.isDone()) {
                System.out.println(futureTask.get());
                break;
            } else {
                //暂停毫秒
                try {
                    TimeUnit.MILLISECONDS.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("正在处理中,不要再催了,越催越慢 ,再催熄火");
            }
        }
    }
}

idDone()轮询

轮询的方式会消耗无谓的CPU资源,而且也不见得能及时的得到计算结果;如果想要异步获取结果,通常都会以轮询的方式去获取结果,尽量不要阻塞。

结论

Future对于结果的获取不是很友好,只能通过阻塞或轮询的方式得到任务的结果。

想完成一些复杂的任务

对于简单的业务场景使用Future是完全的OK的

Future就显得有些力不从心了;

回调通知

应对Future的完成时间,完成了可以告诉我,也就是我们的回调通知;通过轮询的方式去判断任务是否完成这样非常占CPU,并且代码也不优雅。

创建异步任务

Future+线程池配合

多个任务前后依赖可以组合处理

1、想将多个异步任务的计算结果组合起来,后一个异步任务的计算结果需要前一个异步任务的值。将两个或多个异步计算合成一个异步计算,这几个异步计算互相独立,同时后面这个有依赖前一个处理的结果。

2、对计算速度选最快;当Future集合中某个任务最快结束时,返回结果,返回第一名处理结果。

。。。。。。

再这样的场景下,再使用Future之前提供的那点API就囊中羞涩,处理起来不够优雅,这时候还是让CompletableFuture以声明式的方式优雅的处理这些需求。

从i到i++

Future能干的,CompletableFutrue都能干。

CompletableFuture对Future的改进

CompletableFuture为什么会出现

get()方法在Future计算完成之前会一直在阻塞状态下,isDone()方法容易消耗CPU资源,对于真正的异步处理我们希望是可以通过传入回调函数,在Future结束时自动调用该回调函数,这样,我们就不用等待结果。

阻塞的方式和异步编程的设计理念相违背,而轮询的方式会消耗无谓的CPU资源。因此,JDK8设计出CompletableFuture。

CompletableFuture提供了一种观察者模式类似的机制,可以让任务执行完成后通知监听的一方。

CompletableFuture和CompletionStage源码分别介绍

类架构说明

第二章:CompletableFuture_第2张图片

接口CompletionStage 

是什么?

第二章:CompletableFuture_第3张图片

代表异步计算过程中的某一个阶段,一个阶段完成以后可能会触发另外一个阶段,有些类似Linux系统的管道分隔符传参数。

类CompletableFuture 

是什么?

第二章:CompletableFuture_第4张图片

核心的四个静态方法,来创建一个异步任务 

runAsync无返回值

public static CompletableFutrue runAsync(Runnable runnable);

public static CompletableFuture runAsync(Runnable runnable, Executor executor);

supplyAsync有返回值

public static  CompletableFuture supplyAsync(Supplier supplier);

public static  CompletableFuture supplyAsync(Supplier supplier, Executor executor );

上述Executor executor参数说明

没有指定Executor的方法,直接使用默认的ForkJoinPool.commonPool()作为它的线程池执行异步代码。如果指定线程池,则使用我们自定义的或者特别指定的线程池执行异步代码。

无返回值

package com.lzx.juc.cf;

import java.util.concurrent.*;

/**
 * @author admin
 */
public class CompletableFutureBuildDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        CompletableFuture completableFuture = CompletableFuture.runAsync(() -> {
            System.out.println(Thread.currentThread().getName());

            //暂停几秒钟线程
            try { 
                TimeUnit.SECONDS.sleep(1); 
            } catch (InterruptedException e) {
                e.printStackTrace(); 
            }
        }, threadPool);

        System.out.println(completableFuture.get());

        threadPool.shutdown();
    }

}

有返回值

package com.lzx.juc.cf;

import java.util.concurrent.*;

/**
 * @author admin
 */
public class CompletableFutureBuildDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName());

            //暂停几秒钟线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            return "hello supplyAsync";
        }, threadPool);

        System.out.println(completableFuture.get());

        threadPool.shutdown();
    }
}

Code之通用演示减少阻塞和轮询

从Java8开始引入了CompletableFuture,它是Future的功能增强版,减少阻塞和轮询,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。

package com.lzx.juc.cf;

import java.util.concurrent.*;

/**
 * @author admin
 */
public class CompletableFutureUseDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {

        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        try {
            CompletableFuture.supplyAsync(() -> {
                System.out.println(Thread.currentThread().getName() + "----come in");
                int result = ThreadLocalRandom.current().nextInt(10);

                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                System.out.println("-----1秒钟后出结果:" + result);

                if (result > 2) {
                    int i = 10 / 0;
                }

                return result;
            }, threadPool).whenComplete((v, e) -> {
                if (e == null) {
                    System.out.println("-----计算完成,更新系统UpdateValue:" + v);
                }
            }).exceptionally(e -> {
                e.printStackTrace();
                System.out.println("异常情况:" + e.getCause() + "\t" + e.getMessage());
                return null;
            });

            System.out.println(Thread.currentThread().getName() + "线程先去忙其它任务");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            threadPool.shutdown();
        }


        //主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:暂停3秒钟线程
        //try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }

    }

    private static void future1() throws InterruptedException, ExecutionException {
        CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "----come in");
            int result = ThreadLocalRandom.current().nextInt(10);

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("-----1秒钟后出结果:" + result);
            return result;
        });

        System.out.println(Thread.currentThread().getName() + "线程先去忙其它任务");

        System.out.println(completableFuture.get());
    }

}

第二章:CompletableFuture_第5张图片

解释下为什么默认的线程池关闭,自定义的线程池记得关闭。

CompletableFuture的优点

异步任务结束时,会自动回调某个对象方法;

主线程设置好回调后,不再关心异步任务的执行,异步任务之间可以顺序执行;

异步任务出错时,会自动回调某个对象的方法;

案例精讲-从电商网站的比价需求说开去

先看看大厂面试题

第二章:CompletableFuture_第6张图片

函数式编程已经主流

Lambda表达式+Stream流失调用+Chain链式调用+Java8函数式编程。

Runnable

Runnable已经说过很多次了,无参数,无返回值。

Function

Function接受一个参数,并且有返回值。

Consumer

Consumer消费型函数接口,接受一个参数,没有返回值。

BiConsumer

BiConsumer消费型函数接口,接受两个参数(Bi,英文单词词根,代表两个的意思),没有返回值。

Supplier

Supplier供给型函数接口,没有参数,有一个返回值。

小总结

第二章:CompletableFuture_第7张图片

先说说join和get对比 

join与get在功能上几乎没有什么区别,区别在云get在编译期会有抛出检查异常,而join不会。

说说你过去工作中的项目亮点?大厂业务需求说明?

切记,功能->性能,先满足功能的完成,再到性能的完善。

电商网站比价需求分析。

1、    需求说明

1.1、    同一款产品,同时搜索出同款产品在各大电商平台的售价;

1.2、    同一款产品,同时搜索出本产品在同一个电商平台下,各个入驻卖家售价是多少?

2、输出返回:

出来结果希望是同款产品的在不同地方的价格清单列表,返回一个List

《mysql》in jd price is 88.5

《mysql》 in dangdang price is 86.11

《mysql》 in Taobao price is 90.43

3、解决方案,比对同一个商品在各个平台上的价格,要求获得一个清单列表

1、step by step,按部就班,查完京东查淘宝,查完淘宝查天猫……

2、all in,万箭齐发,一口气对线程异步任务同时查询。

一波流Java8函数式编程带走-比价案例实战Case

package com.lzx.juc.cf;

import lombok.*;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author admin
 * 

* 案例说明:电商比价需求,模拟如下情况: *

* 1需求: * 1.1 同一款产品,同时搜索出同款产品在各大电商平台的售价; * 1.2 同一款产品,同时搜索出本产品在同一个电商平台下,各个入驻卖家售价是多少 *

* 2输出:出来结果希望是同款产品的在不同地方的价格清单列表,返回一个List * 《mysql》 in jd price is 88.05 * 《mysql》 in dangdang price is 86.11 * 《mysql》 in taobao price is 90.43 *

* 3 技术要求 * 3.1 函数式编程 * 3.2 链式编程 * 3.3 Stream流式计算 */ public class CompletableFutureMallDemo { static List list = Arrays.asList( new NetMall("jd"), new NetMall("dangdang"), new NetMall("taobao"), new NetMall("pdd"), new NetMall("tmall") ); /** * step by step 一家家搜查 * List ----->map------> List * */ public static List getPrice(List list, String productName) { //《mysql》 in taobao price is 90.43 return list .stream() .map(netMall -> String.format(productName + " in %s price is %.2f", netMall.getNetMallName(), netMall.calcPrice(productName))) .collect(Collectors.toList()); } /** * List ----->List>------> List * */ public static List getPriceByCompletableFuture(List list, String productName) { return list.stream().map(netMall -> CompletableFuture.supplyAsync(() -> String.format(productName + " in %s price is %.2f", netMall.getNetMallName(), netMall.calcPrice(productName)))) .collect(Collectors.toList()) .stream() .map(CompletableFuture::join) .collect(Collectors.toList()); } public static void main(String[] args) { long startTime = System.currentTimeMillis(); List list1 = getPrice(list, "mysql"); for (String element : list1) { System.out.println(element); } long endTime = System.currentTimeMillis(); System.out.println("----costTime: " + (endTime - startTime) + " 毫秒"); System.out.println("--------------------"); long startTime2 = System.currentTimeMillis(); List list2 = getPriceByCompletableFuture(list, "mysql"); for (String element : list2) { System.out.println(element); } long endTime2 = System.currentTimeMillis(); System.out.println("----costTime: " + (endTime2 - startTime2) + " 毫秒"); } } class NetMall { @Getter private String netMallName; public NetMall(String netMallName) { this.netMallName = netMallName; } public double calcPrice(String productName) { try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0); } }

CompletableFuture常用方法

1、获得结果和触发计算

获取结果

public T get();---不见不散

public T get(long timeout, TimeUnit unit);---过时不候

public T join();

public T getNow(T valueIfAbsent);----没有计算完成的情况下,给一个替代的结果;立即获取结果不阻塞(计算完,返回计算完成后的结果;没计算完,返回设定的valueIfAbsent值)。

public boolean complete(T value);----主动触发计算;是否打断get方法立即反回括号值。

package com.lzx.juc.cf;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;

/**
 * @author admin
 */
public class CompletableFutureAPIDemo {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        group1();
    }

    /**
     * 获得结果和触发计算
     *
     */
    private static void group1() throws InterruptedException, ExecutionException {
        CompletableFuture 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());

        //暂停几秒钟线程
        //try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }

        //System.out.println(completableFuture.getNow("xxx"));
        System.out.println(completableFuture.complete("completeValue") + "\t" + completableFuture.get());
    }

}

2、对计算结果进行处理 

thenApply()----计算结果存在依赖关系,这两个线程串行化。

package com.lzx.juc.cf;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author admin
 */
public class CompletableFutureAPI2Demo {

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

        CompletableFuture.supplyAsync(() -> {
            //暂停几秒钟线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("111");
            return 1;
        }, threadPool).thenApply(f -> {
            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.getMessage());
            return null;
        });

        System.out.println(Thread.currentThread().getName() + "----主线程先去忙其它任务");

        threadPool.shutdown();
    }

}

异常相关;由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停。

handle()----计算结果存在依赖关系,这两个线程串行化。

package com.bilibili.juc.cf;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @author admin
 */
public class CompletableFutureAPI2Demo {
    
    public static void main(String[] args) {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);

        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;
        }).handle((f, e) -> {
            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.getMessage());
            return null;
        });

        System.out.println(Thread.currentThread().getName() + "----主线程先去忙其它任务");

        threadPool.shutdown();
    }
}

异常相关;有异常也可以往下一步走,根据带的异常参数可以进一步处理。

总结

第二章:CompletableFuture_第8张图片

3、对计算结果进行消费 

接收任务的处理结果,并消费处理,无返回结果。

thenAccept();

package com.lzx.juc.cf;

import java.util.concurrent.CompletableFuture;

/**
 * @author admin
 */
public class CompletableFutureAPI3Demo {

    public static void main(String[] args) {
        CompletableFuture.supplyAsync(() -> 1)
                .thenApply(f -> f + 2)
                .thenApply(f -> f + 3)
                .thenAccept(System.out::println);
    }

}

对比补充

Code之任务之间的顺序执行

thenRun()

thenRun(Runnable runnable);---任务A执行完成执行B,并且B不需要A的结果。

thenAccept()

thenAccpet(Consumer action);---任务A执行完执行B,B需要A的结果,但是任务B无返回值

thenApply()

thenApply(Function fn);---任务A执行完执行B,B需要A的结果,同时任务B有返回值

code

package com.lzx.juc.cf;

import java.util.concurrent.CompletableFuture;

/**
 * @author admin
 */
public class CompletableFutureAPI3Demo {

    public static void main(String[] args) {
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(System.out::println).join());
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(r -> r + "resultB").join());
    }

}

CompletableFuture和线程池说明

以thenRun和thenRunAsync为例,有什么区别?

package com.lzx.juc.cf;

import java.util.concurrent.*;

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

        try {
            CompletableFuture 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();
        }
    }
    
}

小总结

1、没有传入自定义线程池,都用默认线程池ForkJoinPool。

2、传入了一个自定义线程池,如果你执行第一个任务的时候,传入了一个自定义线程池:

调用thenRun方法执行第二个任务时,则第二个任务和第一个任务是共用同一个线程池。

调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoinPoo线程池。

3、备注

有可能处理太快,系统优化切换原则,直接使用main线程处理。

其他如:thenAccept和thenAcceptAsync,thenApply和thenApplyAsync等,它们之间的区别也是同理。

调用thenRunAsync执行第二个任务时,则第一个任务使用的是你自己传入的线程池,第二个任务使用的是ForkJoinPoo线程池。

源码分析

第二章:CompletableFuture_第9张图片

4、对计算速度进行选用 

谁快用谁

applyToEither()

package com.lzx.juc.cf;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

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

        CompletableFuture playB = CompletableFuture.supplyAsync(() -> {
            System.out.println("B come in");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "playB";
        });

        CompletableFuture result = playA.applyToEither(playB, f -> f + " is winer");

        System.out.println(Thread.currentThread().getName() + "\t" + "-----: " + result.join());
    }
}

5、对计算结果进行合并

两个CompletableStage任务都完成后,最终能把两个任务的结果一起交给thenCombine来处理;先完成的先等着,等待其他分支任务。

thenCombine()

Code标准版,好理解先拆分

package com.lzx.juc.cf;

import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * @author admin
 */
public class CompletableFutureCombineDemo {
    
    public static void main(String[] args) {
        CompletableFuture completableFuture1 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t ---启动");
            //暂停几秒钟线程
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 10;
        });

        CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t ---启动");
            //暂停几秒钟线程
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 20;
        });

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

        System.out.println(result.join());

    }

}

Code表达式

第二章:CompletableFuture_第10张图片

你可能感兴趣的:(#,JUC,java,开发语言)