多线程进阶学习02------Future异步任务

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
云数据库 RDS PostgreSQL,集群系列 2核4GB
简介: 多线程进阶学习02------Future异步任务

异步任务即开辟分支任务,不阻塞主线程。Tips:异步线程的创建是纳秒级别

FutureTask

创建方式

// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
    return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();

缺点

1、获取值get阻塞

  • futureTask.get():get方法会阻塞主线程,一般要把get方法放到最后
  • futureTask.get(3,TimeUnit.SECONDS):假如我不愿意等待很长时间,过时不候直接抛出TimeOutException。
    其他程序调用的时候可以捕获超时异常,相当于变相的做了止损操作
FutureTask<Integer> task = new FutureTask<>(() -> {
    TimeUnit.SECONDS.sleep(10);
    return 100;
});
new Thread(task, "t3").start();
System.out.println(task.get());

2、轮训获取值isDone

  • 不断给主线程续命以等待异步线程的返回
  • 徒劳消耗cpu资源
FutureTask<Integer> task = new FutureTask<>(() -> {
    TimeUnit.SECONDS.sleep(10);
    return 100;
});
new Thread(task, "t3").start();
while (!task.isDone()) {
    System.out.println(task.get());
}

CompletableFuture

原有的FutureTask类,get()方法会导致阻塞,isDone()轮询也占用cpu,并且能用的api较少,对于以上缺点,jdk8推出了CompletableFuture。

  1. 在Java8中,CompletableFuture提供了非常强大的Future的扩展功能,可以帮助我们简化异步编程的复杂性,并且提供了函数式编程的能力,可以通过回调的方式处理计算结果,也提供了转换和组合CompletableFuture的方法
  1. 它可能代表一个明确完成的Future,也有可能代表一个完成阶段(CompletionStage),它支持在计算完成以后触发一些函数或执行某些动作

案例展示

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 提供了四个静态方法来创建一个异步操作

  • runAsync方法不支持返回值.适用于多个接口之间没有任何先后关系
  • supplyAsync可以支持返回值,我们一般用supplyAsync来创建

比如在一个方法中,调用6个接口,接口A的结果需要作为接口B的入参,这个时候适合用带返回值的构造

//runAsync方法不支持返回值
public static CompletableFuture<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的方法会使用ForkJoinPool.commonPool() 作为它的线程池执行异步代码。如果指定线程池,则使用指定的线程池运行。以下所有的方法都类同

public class CompletableFutureTest {
    public static void main(String[] args) throws Exception{
        ThreadPoolExecutor executor = new ThreadPoolExecutor(2,
                5,
                2L,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(3));
        //(1). CompletableFuture.runAsync(Runnable runnable);
        CompletableFuture future1=CompletableFuture.runAsync(()->{
            System.out.println(Thread.currentThread().getName()+"*********future1 coming in");
        });
        //这里获取到的值是null
        System.out.println(future1.get());
        //(2). CompletableFuture.runAsync(Runnable runnable,Executor executor);
        CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
            //ForkJoinPool.commonPool-worker-9 
            System.out.println(Thread.currentThread().getName() + "\t" + "*********future2 coming in");
        }, executor);
        //(3).public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
        CompletableFuture<Integer> future3 = CompletableFuture.supplyAsync(() -> {
            //pool-1-thread-1
            System.out.println(Thread.currentThread().getName() + "\t" + "future3带有返回值");
            return 1024;
        });
        System.out.println(future3.get());
        //(4).public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
        CompletableFuture<Integer> future4 = CompletableFuture.supplyAsync(() -> {
            System.out.println(Thread.currentThread().getName() + "\t" + "future4带有返回值");
            return 1025;
        }, executor);
        System.out.println(future4.get());
        //关闭线程池
        executor.shutdown();
    }
}

获取结果

  1. public T get( ) 不见不散(会抛出异常) 只要调用了get( )方法,不管是否计算完成都会导致阻塞
  2. public T get(long timeout, TimeUnit unit) 过时不候
  3. public T getNow(T valuelfAbsent):没有计算完成的情况下,给我一个替代结果计算完,返回计算完成后的结果、没算完,返回设定的valuelfAbsent
  4. public T join( ):join方法和get( )方法作用一样,不同的是,join方法不抛出异常
  5. public boolean complete(T value) 是否打断get方法立刻返回括号值
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());
    }

处理计算结果

  1. public CompletableFuture thenApply 计算结果存在依赖关系,这两个线程串行化
    由于存在依赖关系(当前步错,不走下一步),当前步骤有异常的话就叫停
  2. public CompletableFuture handle(BiFunction<? super T, Throwable, ? extends U> fn):有异常也可以往下一步走,根据带的异常参数可以进一步处理
  3. public CompletableFuture whenComplete( BiConsumer<? super T, ? super Throwable> action); 任务完成或者异常时运行action
  4. public CompletableFuture whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor); 任务完成或者异常时运行action,j可配置线程池
  5. public CompletableFuture exceptionally(Function<Throwable, ? extends T> fn); 有异常本次就会执行,否则不执行
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();

whenComplete与handle的区别在于,它不参与返回结果的处理,把它当成监听器即可

即使异常被处理,在CompletableFuture外层,异常也会再次复现

消费计算结果

  1. thenRun(Runnable runnable) 任务A执行完执行B,并且B不需要A的结果
  2. CompletableFuture thenAccept(Consumer<? super T> action) 任务A执行完成执行B,B需要A的结果,但是任务B无返回值
  3. public CompletableFuture thenApply(Function<? super T,? extends U> fn) 任务A执行完成执行B,B需要A的结果,同时任务B有返回值
        CompletableFuture.supplyAsync(() -> {
            return 1;
        }).thenApply(f -> {
            return f+2;
        }).thenApply(f -> {
            return f+3;
        }).thenAccept(r -> System.out.println(r));
        // 任务A执行完执行B,并且B不需要A的结果
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenRun(() -> {}).join());
        // 任务A执行完成执行B,B需要A的结果,但是任务B无返回值
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenAccept(resultA -> {}).join());
        // 任务A执行完成执行B,B需要A的结果,同时任务B有返回值
        System.out.println(CompletableFuture.supplyAsync(() -> "resultA").thenApply(resultA -> resultA + " resultB").join());

对计算速度进行选用

public CompletableFuture applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn)

这个方法表示的是,谁快就用谁的结果,类似于我们在打跑得快,或者麻将谁赢了就返回给谁

        //这个方法表示的是,谁快就用谁的结果,类似于我们在打跑得快,或者麻将谁赢了就返回给谁
        //public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn);
        //下面这个在第一个中停留1s,在第二种停留2s,返回的结果是1
        System.out.println(CompletableFuture.supplyAsync(() -> {
            //暂停几秒钟线程
            try { TimeUnit.SECONDS.sleep(1);  } catch (InterruptedException e) {e.printStackTrace();}
            return 1;
        }).applyToEither(CompletableFuture.supplyAsync(() -> {
            try { TimeUnit.SECONDS.sleep(2);  } catch (InterruptedException e) {e.printStackTrace();}
            return 2;
        }), r -> {
            return r;
        }).join());
        //暂停几秒钟线程
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }

applyToEither:两个任务有一个执行完成,获取它的返回值,处理任务并有新的返回值

acceptEither:两个任务有一个执行完成,获取它的返回值,处理任务,没有新的返回值

runAfterEither:两个任务有一个执行完成,不需要获取 future 的结果,处理任务,也没有返回值

对计算结果进行合并

public <U,V> CompletableFuture thenCombine(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)

两个CompletionStage任务都完成后,最终把两个任务的结果一起交给thenCombine来处理

先完成的先等着,等待其他分支任务

        //public <U,V> CompletableFuture<V> thenCombine
        //(CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn)
        //两个CompletionStage任务都完成后,最终把两个任务的结果一起交给thenCombine来处理
        //先完成的先等着,等待其他分支任务
        System.out.println(CompletableFuture.supplyAsync(() -> {
            return 10;
        }).thenCombine(CompletableFuture.supplyAsync(() -> {
            return 20;
        }), (r1, r2) -> {
            return r1 + r2;
        }).thenCombine(CompletableFuture.supplyAsync(() -> {
            return 30;
        }), (r3, r4) -> {
            return r3 + r4;
        }).join());
        System.out.println(CompletableFuture.supplyAsync(() -> {
            return 10;
        }).thenCombine(CompletableFuture.supplyAsync(() -> {
            return 20;
        }), (r1, r2) -> {
            return r1 + r2;
        }).join());

两任务组合,都要完成

        CompletableFuture.supplyAsync(() -> {
            return 10;
        })
                .thenAcceptBoth(CompletableFuture.supplyAsync(() -> {
                    return 20;
                }), (r1, r2) -> {
                    System.out.println(r1);//10
                    System.out.println(r2);//20
                });

多任务组合

(public static CompletableFuture allOf(CompletableFuture<?>… cfs))

allOf:等待所有任务完成


(public static CompletableFuture anyOf(CompletableFuture<?>… cfs))

anyOf:只要有一个任务完成

 CompletableFuture<String> futureImg = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的图片信息");
            return "hello.jpg";
        });
        CompletableFuture<String> futureAttr = CompletableFuture.supplyAsync(() -> {
            System.out.println("查询商品的属性");
            return "黑色+256G";
        });
        CompletableFuture<String> futureDesc = CompletableFuture.supplyAsync(() -> {
            try { TimeUnit.SECONDS.sleep(3);  } catch (InterruptedException e) {e.printStackTrace();}
            System.out.println("查询商品介绍");
            return "华为";
        });
        //需要全部完成
//        futureImg.get();
//        futureAttr.get();
//        futureDesc.get();
        //CompletableFuture<Void> all = CompletableFuture.allOf(futureImg, futureAttr, futureDesc);
        //all.get();
        CompletableFuture<Object> anyOf = CompletableFuture.anyOf(futureImg, futureAttr, futureDesc);
        anyOf.get();
        System.out.println(anyOf.get());
        System.out.println("main over.....");

案例演示

电商比价需求

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

同一款产品,同时搜索出本产品在某一个电商平台下,各个入驻门店的售价是多少

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

in jd price is 88.05

in pdd price is 86.11

in taobao price is 90.43

public class CompletableFutureNetMallDemo {
    static List<NetMall> list = Arrays.asList(
            new NetMall("jd"),
            new NetMall("pdd"),
            new NetMall("taobao"),
            new NetMall("dangdangwang"),
            new NetMall("tmall"));
    //同步 ,step by step
    /**
     * List<NetMall>  ---->   List<String>
     * @param list
     * @param productName
     * @return
     */
    public static List<String> getPriceByStep(List<NetMall> list,String productName) {
        return list
                .stream()
                .map(netMall -> String.format(productName + " in %s price is %.2f", netMall.getMallName(),
                                netMall.calcPrice(productName)))
                .collect(Collectors.toList());
    }
    //异步 ,多箭齐发
    /**
     * List<NetMall>  ---->List<CompletableFuture<String>> --->   List<String>
     * @param list
     * @param productName
     * @return
     */
    public static List<String> getPriceByASync(List<NetMall> list,String productName) {
        return list
                .stream()
                .map(netMall ->
                        CompletableFuture.supplyAsync(() ->
                        String.format(productName + " is %s price is %.2f", netMall.getMallName(), 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 = getPriceByStep(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 = getPriceByASync(list, "mysql");
        for (String element : list2) {
            System.out.println(element);
        }
        long endTime2 = System.currentTimeMillis();
        System.out.println("----costTime: "+(endTime2 - startTime2) +" 毫秒");
    }
}
@Data
@AllArgsConstructor
class NetMall {
    private String mallName;
    public double calcPrice(String productName) {
        //检索需要1秒钟
        try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); }
        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
    }
}
/*
  mysql in jd price is 110.59
  mysql in pdd price is 110.23
  mysql in taobao price is 110.04
  mysql in dangdangwang price is 110.08
  mysql in tmall price is 109.91
  ----costTime: 5030 毫秒
  mysql is jd price is 109.07
  mysql is pdd price is 109.47
  mysql is taobao price is 109.04
  mysql is dangdangwang price is 110.09
  mysql is tmall price is 110.72
  ----costTime: 1021 毫秒
**/

100个任务都返回一个数字且运行都要时间,使用多线程快速求和

三种写法,自我感受

package com.bilibili.juc.study;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
/**
 * CompletableFuture 多任务合并
 */
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        long start = System.currentTimeMillis();
        ArrayList<Integer> lists = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            lists.add(1);
        }
        System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism","100");
        int sum = lists.stream().parallel().mapToInt(integer -> {
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return integer;
        }).sum();
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start));
    }
    public static void fangshi1() {
        long start = System.currentTimeMillis();
        ExecutorService threadPool = Executors.newFixedThreadPool(50);
        ArrayList<Integer> lists = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            lists.add(1);
        }
        int sum = lists.stream()
                .map(integer -> CompletableFuture.supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return integer;
                }, threadPool))
                .collect(Collectors.toList())
                .stream()
                .map(CompletableFuture::join)
                .mapToInt(value -> value)
                .sum();
        System.out.println(sum);
        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start));
        threadPool.shutdown();
    }
    public static void fangshi2(){
        long start = System.currentTimeMillis();
        ExecutorService threadPool = Executors.newFixedThreadPool(100);
        ArrayList<Integer> lists = new ArrayList<>();
        for (int i = 1; i <= 100; i++) {
            lists.add(1);
        }
        List<CompletableFuture<Integer>> completableFutureList = lists.stream()
                .map(integer -> CompletableFuture.supplyAsync(() -> {
                    try {
                        TimeUnit.SECONDS.sleep(2);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    return integer;
                }, threadPool))
                .collect(Collectors.toList());
        CompletableFuture<Void> completableFuture = CompletableFuture.allOf(completableFutureList
                .toArray(new CompletableFuture[completableFutureList.size()]));
        CompletableFuture<List<Integer>> listCompletableFuture = completableFuture.thenApply(v -> {
            return completableFutureList.stream().map(CompletableFuture::join).collect(Collectors.toList());
        });
        CompletableFuture<Long> longCompletableFuture = listCompletableFuture.thenApply(list -> {
            return list.stream().count();
        });
        System.out.println(longCompletableFuture.join());
        long end = System.currentTimeMillis();
        System.out.println("耗时:" + (end - start));
        threadPool.shutdown();
    }
}
    }


相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
1月前
|
监控 Java 调度
【Java学习】多线程&JUC万字超详解
本文详细介绍了多线程的概念和三种实现方式,还有一些常见的成员方法,CPU的调动方式,多线程的生命周期,还有线程安全问题,锁和死锁的概念,以及等待唤醒机制,阻塞队列,多线程的六种状态,线程池等
105 6
【Java学习】多线程&JUC万字超详解
|
21天前
|
设计模式 缓存 Java
谷粒商城笔记+踩坑(14)——异步和线程池
初始化线程的4种方式、线程池详解、异步编排 CompletableFuture
谷粒商城笔记+踩坑(14)——异步和线程池
|
2月前
|
缓存 Java
异步&线程池 线程池的七大参数 初始化线程的4种方式 【上篇】
这篇文章详细介绍了Java中线程的四种初始化方式,包括继承Thread类、实现Runnable接口、实现Callable接口与FutureTask结合使用,以及使用线程池。同时,还深入探讨了线程池的七大参数及其作用,解释了线程池的运行流程,并列举了四种常见的线程池类型。最后,阐述了在开发中使用线程池的原因,如降低资源消耗、提高响应速度和增强线程的可管理性。
异步&线程池 线程池的七大参数 初始化线程的4种方式 【上篇】
|
2月前
|
Java 数据库
异步&线程池 CompletableFuture 异步编排 实战应用 【终结篇】
这篇文章通过一个电商商品详情页的实战案例,展示了如何使用`CompletableFuture`进行异步编排,以解决在不同数据库表中查询商品信息的问题,并提供了详细的代码实现和遇到问题(如图片未显示)的解决方案。
异步&线程池 CompletableFuture 异步编排 实战应用 【终结篇】
|
2月前
|
存储 监控 Java
|
2月前
|
Java
异步&线程池 CompletableFuture 异步编排 【下篇】
这篇文章深入探讨了Java中的`CompletableFuture`类,解释了如何创建异步操作、使用计算完成时的回调方法、异常处理、串行化方法、任务组合以及多任务组合的使用方式,并通过代码示例展示了各种场景下的应用。
异步&线程池 CompletableFuture 异步编排 【下篇】
|
2月前
|
前端开发 JavaScript 大数据
React与Web Workers:开启前端多线程时代的钥匙——深入探索计算密集型任务的优化策略与最佳实践
【8月更文挑战第31天】随着Web应用复杂性的提升,单线程JavaScript已难以胜任高计算量任务。Web Workers通过多线程编程解决了这一问题,使耗时任务独立运行而不阻塞主线程。结合React的组件化与虚拟DOM优势,可将大数据处理等任务交由Web Workers完成,确保UI流畅。最佳实践包括定义清晰接口、加强错误处理及合理评估任务特性。这一结合不仅提升了用户体验,更为前端开发带来多线程时代的全新可能。
29 0
|
2月前
|
Dart API C语言
Dart ffi 使用问题之想在C/C++中创建异步线程来调用Dart方法,如何操作
Dart ffi 使用问题之想在C/C++中创建异步线程来调用Dart方法,如何操作
|
2月前
|
Cloud Native Java 调度
项目环境测试问题之线程同步器会造成执行完任务的worker等待的情况如何解决
项目环境测试问题之线程同步器会造成执行完任务的worker等待的情况如何解决
|
2月前
|
存储 监控 Java
Java多线程优化:提高线程池性能的技巧与实践
Java多线程优化:提高线程池性能的技巧与实践
64 1