关于java:JAVA并发编程Future和CompletableFuture的简介与使用

1次阅读

共计 4001 个字符,预计需要花费 11 分钟才能阅读完成。

1.Future 类简介

2.Future 类的应用

3.CompletableFuture 类的简介

4.CompletableFuture 类的的应用

5. 总结

1.Future 类简介
咱们之前在这篇文章 JAVA 并发编程——Callable 接口和 FutureTask 简介和应用中说过,Future 就是能够取得返回值的一个异步解决线程类。
在之前无论是继承 Thread 类还是实现 Runnable 接口,咱们始终 无奈失去线程的返回值,然而 Future 就能够,Future 是领有返回值的线程,这样说咱们可能还不明确,咱们用一个生产的案例来阐明一下这个例子。

2.Future 类的应用
假如咱们当初有这样一个需要:

咱们先来看一个页面:

这是一个网上找来的数据大屏的图片,如果这个屏幕的数据要你查问出并且返回,你会怎么做?

咱们晓得大屏展现的区域不同,要查问的业务逻辑也就不一样,能够将它分成如下这几块区域:

如果当初是一名老手,可能会 一个区域一个区域串行地去查问,区域一查完了查区域二,紧接着区域三,而后再返回给前端,这样效率不高。

改良办法:多个区域同步查问,最初对立返回给前端(多箭齐发)。

这么说还是比拟形象,咱们用一张图片举例:

用这张图片应该就能解释了,接下来咱们应用 Future 类进行代码展现一下:

public DataResult queryData() throws ExecutionException, InterruptedException, TimeoutException {DataResult result = new DataResult();
        // 查问区域 1
        FutureTask<Integer> allHumanNum = new FutureTask<>(() -> queryHumanNum());
        // 应用线程池执行
        CalculateThreadPoolConfig.getThreadPoolExecutor().submit(allDoctorNum);
        // 查问区域 2
        FutureTask<Integer> allMaleNum = new FutureTask<>(() -> queryAllMaleNum());
       // 应用线程池执行 CalculateThreadPoolConfig.getThreadPoolExecutor().submit(queryInvitedDoctorNum);
       // 顺次类推
       //......

        // 拼接后果集
        result.setAllHumanNum(allHumanNum.get(5, TimeUnit.SECONDS));
        result.setAllMaleNum(allMaleNum.get(5, TimeUnit.SECONDS));
        return result;
    }

能够看出,咱们无论查问多少个数据集,只用破费查问工夫最长的那一个数据集的工夫就够了,就实现了同步并行查问的成果,大大减少查问工夫。

不过咱们要留神一下这行代码:

allHumanNum.get(5, TimeUnit.SECONDS);

get 的意思是始终阻塞返回后果 ,然而加了一个数字和单位就代表,只期待 5 秒,不然会抛异样。
咱们工作的时候,如果须要调用他人的接口返回数据,然而不晓得对方要计算多久,可能对方一个接口就把咱们写的整个性能给拖垮 。为了防止这种状况, 工作中为了爱护本人,咱们须要学会加一层保险,如果对方的接口如果五秒没有返回就过时不候了。

3.CompletableFuture 类的简介

介绍了后面的 FutreTask 类,咱们晓得这尽管是一个很好用的类,然而也是有很多的毛病的,比方:
1.get()容易阻塞
2. 如果想要把多个后果汇合并成一个后果集,就须要很多 FutureTask
3.FutureTask 只能用于单步的简略计算,不能将两个异步计算合成一个异步计算,这两个异步计算相互独立,同时第二个又依赖第一个的后果。
4.当 Future 汇合中某个工作最快完结时,返回后果。
等等

毛病还是比拟多的,这个时候咱们就能够应用 CompletableFuture 类!

CompletableFuture 补救了 Future 模式的毛病。它能够将多个后果汇合并成一个后果集,又能够将一个异步工作分成好几个阶段进行计算,前面的后果又依赖之前的后果等等。

总而言之,FutureTask 能办到的事,CompletableFuture 也能办到,同时 CompletableFuture 还能办到更多的事件!

咱们先搜寻一下 CompletableFuture 这个类

它实现了 Future 接口和 CompletionStage 接口。

CompletionStage:代表异步计算过程中的某一个阶段,一个阶段实现当前可能会触发另外一个阶段。

咱们认真看 CompletionStage 领有的办法,依据 thenApply,whenComplete,exceptionally 等办法,能够大略看出这个类能够对一个计算的每个阶段进行编程,先执行完一步,(依据 thenApply)再执行下一步,(whenComplete)最初运行完了会怎么样,(exceptionally)抛出异样会怎么样等等。

如同就像一条调用链一样,这就是所谓的链式编程。

接下来咱们用一个案例用 CompletableFuture 进行实战。

4.CompletableFuture 类的的应用

假如咱们当初有一个电商比价需要:
咱们要从各大网站上收集 Think in java 这本书的价格,开多个线程从各大网站收集,收集完之后输入信息到控制台,线程执行结束也要输入信息,请应用 CompletableFuture 和链式编程来进行解决。

咱们接下来开始编程:


public class CompletableFutureNextMallDemo {
 // 这是须要收集的价格网站
 static List<NetMall> list = Arrays.asList(new NetMall("jd"),
            new NetMall("pdff"),
            new NetMall("tmall"),
            new NetMall("xhs"),
            new NetMall("xy"),
            new NetMall("zh"),
            new NetMall("sf")
    );

}
class NetMall {
    // 这是网站的具体类以及计算价格的办法
    private String mallName;

    public NetMall() {}

    public NetMall(String mallName) {this.mallName = mallName;}

    public String getMallName() {return mallName;}

    public void setMallName(String mallName) {this.mallName = mallName;}

    // 计算价格的办法
    public double calcPrice(String productName) {
        try {
            // 假如这是计算过程
            Thread.sleep(1000);
        } catch (InterruptedException e) {e.printStackTrace();
        }
        return ThreadLocalRandom.current().nextDouble() * 2 + productName.charAt(0);
    }

}

最要害的执行办法

    // 链式调度 多箭齐发
    public static List<String> getPriceByASync(List<NetMall> list, String productName) {

        // 循环遍历所有网站,每一个网站开启一个线程进行计算
        //CompletableFuture.supplyAsync 代表有返回值的执行过程,相当于 Future 的 get(0 办法
        return list.stream().map(netMall -> CompletableFuture.supplyAsync(() -> {return String.format(productName + "in %s price is %.2f", netMall.getMallName(), netMall.calcPrice(productName));
        }
        // 当第一步执行结束后,第二步就是输入书的价格
        ).thenApply(r-> {System.out.println(r);
            return r;
        }
        // 当实现工作之后,输入该网站计算实现
        ).whenComplete((v,e)-> {System.out.println(netMall.getMallName()+"计算实现!");
        }
        // 当抛出异样的时候,输入异样
        ).exceptionally(e->{System.out.println("获取失败,出现异常!");
            e.printStackTrace();
            return null;
        })).collect(Collectors.toList())
           .stream()
           // 最初对立调用 join 办法返回后果
           .map(CompletableFuture::join)
           // 最初合并成一个 List,作为后果返回
           .collect(Collectors.toList());
    }

这里咱们将 CompletableFuture 抽离进去会发现代码会是如下所示:

CompletableFuture
.supplyAsync()// 执行有返回值的异步解决,相当于 FutureTask 的 get()
.thenApply()// 接着执行第二步
.whenComplete()// 当实现时触发的逻辑
.exceptionally();// 当出现异常的时候触发的逻辑

5. 总结

通过明天的学习,咱们理解了 Future 和 CompletableFuture 的根本实践和实际,这自身就是十分好用的两个类,心愿咱们在工作中能多多应用,不过也不要自觉应用多线程,因为多应用一种工具,就会多一份危险。

正文完
 0