第二章:CompletableFuture

news2025/1/11 17:09:54

Future接口理论知识复习

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

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

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

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

Future接口能干什么?

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

代码说话:

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

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

本源的Future接口相关的架构

package com.lzx.juc.cf;

import java.util.concurrent.*;

/**
 * @author admin
 */
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() {
        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<String> futureTask1 = new FutureTask<>(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "task1 over";
        });

        threadPool.submit(futureTask1);

        FutureTask<String> 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<String> 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<String> 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源码分别介绍

类架构说明

接口CompletionStage 

是什么?

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

类CompletableFuture 

是什么?

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

runAsync无返回值

public static CompletableFutrue<Void> runAsync(Runnable runnable);

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

supplyAsync有返回值

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> 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<Void> 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<String> 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<Integer> 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的优点

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

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

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

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

先看看大厂面试题

函数式编程已经主流

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

Runnable

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

Function

Function<T, R>接受一个参数,并且有返回值。

Consumer

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

BiConsumer

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

Supplier

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

小总结

先说说join和get对比 

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

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

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

电商网站比价需求分析。

1、    需求说明

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

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

2、输出返回:

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

《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
 * <p>
 * 案例说明:电商比价需求,模拟如下情况:
 * <p>
 * 1需求:
 * 1.1 同一款产品,同时搜索出同款产品在各大电商平台的售价;
 * 1.2 同一款产品,同时搜索出本产品在同一个电商平台下,各个入驻卖家售价是多少
 * <p>
 * 2输出:出来结果希望是同款产品的在不同地方的价格清单列表,返回一个List<String>
 * 《mysql》 in jd price is 88.05
 * 《mysql》 in dangdang price is 86.11
 * 《mysql》 in taobao price is 90.43
 * <p>
 * 3 技术要求
 * 3.1 函数式编程
 * 3.2 链式编程
 * 3.3 Stream流式计算
 */
public class CompletableFutureMallDemo {

    static List<NetMall> list = Arrays.asList(
            new NetMall("jd"),
            new NetMall("dangdang"),
            new NetMall("taobao"),
            new NetMall("pdd"),
            new NetMall("tmall")
    );

    /**
     * step by step 一家家搜查
     * List<NetMall> ----->map------> List<String>
     *
     */
    public static List<String> getPrice(List<NetMall> 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<NetMall> ----->List<CompletableFuture<String>>------> List<String>
     *
     */
    public static List<String> getPriceByCompletableFuture(List<NetMall> 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<String> 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<String> 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<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());

        //暂停几秒钟线程
        //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();
    }
}

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

总结

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

小总结

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

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

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

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

3、备注

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

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

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

源码分析

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<String> playA = CompletableFuture.supplyAsync(() -> {
            System.out.println("A come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "playA";
        });

        CompletableFuture<String> playB = CompletableFuture.supplyAsync(() -> {
            System.out.println("B come in");
            try {
                TimeUnit.SECONDS.sleep(1);
            } 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、对计算结果进行合并

两个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<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("-----开始两个结果合并");
            return x + y;
        });

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

    }

}

Code表达式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/999260.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JBoss 5.x/6.x 反序列化漏洞复现(CVE-2017-12149)

一、漏洞说明 该漏洞存在于http invoker组件的ReadOnlyAccessFilter的doFilter中&#xff0c;在/invoker/readonly请求中&#xff0c;服务器将用户提交的POST内容进行了Java反序列化 二、搭建环境 cd vulhub/jboss/CVE-2017-12149 docker-compose up -d 三、漏洞验证 访问:80…

海南热带海洋学院金秋悦读《乡村振兴战略下传统村落文化旅游设计》2023新学年许少辉八一新书​

海南热带海洋学院金秋悦读《乡村振兴战略下传统村落文化旅游设计》2023新学年许少辉八一新书​

最新在线IDE流行度最新排名(每月更新)

2023年09月在线IDE流行度最新排名 TOP 在线IDE排名是通过分析在线ide名称在谷歌上被搜索的频率而创建的 在线IDE被搜索的次数越多&#xff0c;人们就会认为它越受欢迎。原始数据来自谷歌Trends 如果您相信集体智慧&#xff0c;那么TOP ODE索引可以帮助您决定在软件开发项目中…

Win10 ping 虚拟机kali 请求超时解决办法

出现这种问题应该是windows休眠导致的 这里我的解决方法是先禁用再启用连接 然后再ping 虚拟机ip和kali ip&#xff0c;发现就可以连上了

zookeeper没有.log日志,只有.out日志

zookeeper没有.log日志&#xff0c;只有.out日志 背景&#xff1a;发现zookeeper没有.log日志&#xff0c;只有.out日志 发现在logs目录下&#xff0c;只有.out文件&#xff0c;且每次重启zk&#xff0c;.out日志都会被覆盖写 为了有完整的log日志&#xff0c;需要如下参数 1…

2023上半年京东运动鞋服市场数据分析(京东数据运营)

大众线下运动生活恢复&#xff0c;掀起新一轮户外潮流&#xff0c;运动热潮迭起。由此产生的运动鞋服及专业装备需求&#xff0c;为运动品牌们带来了诸多增长机会。近日各大运动品牌陆续发布上半年财报&#xff0c;回答了品牌对复苏机遇、发展挑战的应对情况。接下来结合具体数…

LDR6020 USB PD3.1的 3组6通道CC控制SOC芯片介绍和运用

随着USB-IF 协会发布了全新的USB PD3.1规范&#xff0c;该规范将快充功率上限从100 W提升至240 W&#xff0c;充电功率的提升也让USB PD的应用领域更加广泛。 USB PD在手机、笔记本电脑&#xff0c;扩展到便携式设备、物联网设备、智能家居、通信和安防设备、汽车和医疗等领域…

SpringMVC文件的上传下载JRebel的使用

目录 前言 一、JRebel的使用 1.IDea内安装插件 2.激活 3.离线使用 使用JRebel的优势 二、文件上传与下载 1 .导入pom依赖 2.配置文件上传解析器 3.数据表 4.配置文件 5.前端jsp页面 6.controller层 7.测试结果 前言 当涉及到Web应用程序的开发时&…

IE浏览器打开变成别的浏览器怎么办

最近很多朋友发现自己的IE浏览器打开时会自动跳转到别的浏览器上去&#xff0c;又不知道怎么恢复回来。所以今天小编就来告诉大家解决的方法&#xff0c;下面就是详细的教程&#xff0c;想知道的快来看看吧。 IE浏览器打开变成别的浏览器解决方法 方法一 1、打开的浏览器中找…

DataTables.js综合应用开发(ajax筛选查询、同列数据汇总、环比数据)

筛选数据 <!--筛选数据--><div class"layui-card" style"margin-bottom: 50px;"><div class"layui-row"><form class"layui-form layui-col-md12" style"margin: 10px;"><div class"demoTa…

百望云蝉联2023「Cloud 100 China 」榜单 综合实力再获认可

9月7日&#xff0c;2023 Cloud 100 China 榜单于上海中心正式发布&#xff0c;榜单由靖亚资本与崔牛会联合推出&#xff0c;百望云凭借着过硬的综合实力与卓越的技术创新能力&#xff0c;再次荣登榜单&#xff0c;位居第六位。 本届评选&#xff0c;Top 100 企业的数据指标的权…

EMC滤波器对DC电源模块影响有多大?

BOSHIDA EMC滤波器对DC电源模块影响有多大&#xff1f; 随着电子设备的广泛应用&#xff0c;EMC&#xff08;电磁兼容性&#xff09;问题越来越受到关注。而EMC滤波器作为一种常见的电路器件&#xff0c;其作用在于抑制电源的干扰和防止外界干扰的影响。在DC电源模块的设计中&…

直击永悦科技半年报:双轮驱动下的“增长曲线”

詹姆斯卡斯的著作《有限与无限的游戏》中&#xff0c;传递出这样一种观点&#xff1a; “有限的游戏&#xff0c;其目的在于赢得胜利&#xff1b;无限的游戏&#xff0c;却旨在让游戏永远进行下去。有限的游戏在边界内玩&#xff0c;无限的游戏玩的就是边界。” 企业要实现持…

ME21N 采购订单新增页签增强

1、实现效果 根据客制化需求&#xff0c;要在采购订单中新增大量字段&#xff0c;所以要在界面上添加一个单独的页签。效果如下&#xff1a; 2、增强实现 2.1、增强结构 因为是在抬头上边添加&#xff0c;所以增强CI_EKKODB结构 2.2、函数组 仿照Function Group MEPOBADIEX…

vue 项目npm 打包遇到的一些bug记录

问题场景 &#xff1a;npm 的版本正确&#xff0c;nodejs 的版本也是正常的&#xff0c;之前npm run build 打包都正常没问题&#xff0c;但是因为其他原因电脑重装了&#xff0c;环境重新配置了。npm run dev 跑没问题,打包就报错了&#xff0c;信息如下&#xff1a; rc/util…

Java从入门到精通-数组(三)

0. 数组 1. 数组概述 练习1&#xff1a; 创建一个整数数组&#xff0c;存储喜欢的5个数字&#xff0c;并编写代码打印出这些数字。 创建一个整数数组 public class Main {public static void main(String[] args) {// 创建一个整数数组&#xff0c;存储5个喜欢的数字int[] f…

G4学术期刊《中学教学参考》简介及投稿要求

G4学术期刊《中学教学参考》简介及投稿要求 《中学教学参考》为广西教育学院杂志社主办的国内外公开发行刊物&#xff0c;坚持为基础教育服务的宗旨&#xff0c;竭诚为全国教育工作者服务&#xff0c;提供最新教改信息和教育理论成果&#xff0c;为广大教育工作者交流经验、发…

python系列:requests库+BS4库及综合实例

仅供学习、交流使用&#xff0c;不具有任何商业用途&#xff0c;如有问题请及时联系我们以作处理。 文章目录 前言requests安装使用第一种 requests.request("请求方式", ...)第二种 requests.请求方式(参数)URL中传递参数 paramsxx响应内容 r.text二进制响应内容 r.…

从零开始的PICO教程(1)Pico游戏开发项目配置

从零开始的PICO教程&#xff08;1&#xff09;Pico游戏开发项目配置 文章目录 从零开始的PICO教程&#xff08;1&#xff09;Pico游戏开发项目配置一、前言1、大纲 二、成为Pico开发者1、注册 PICO 开发者帐号2、创建组织3、创建应用 三、导入PICO的SDK1、PICO设备开启 “开发者…

一文了解数据科学Notebook

编者按&#xff1a; 主要介绍什么是Notebook&#xff0c;Notebook在数据科学领域的应用的重要性与优势&#xff0c;以及数据科学家/算法团队在选择Notebook时需考虑哪些关键因素。同时&#xff0c;基于Notebook的筛选考量维度&#xff0c;对常见的Notebook进初步对比分析&#…