资讯专栏INFORMATION COLUMN

java并发编程学习16--CompletableFuture(三)

马忠志 / 1674人阅读

摘要:所以很容易出现某一个商店的数据迟迟无法返回的情况。工厂方法接受由对象构成的数组数组中所有的完成后它返回一个对象。异步的可以通过进行合并,无论他们之间是否有依赖关系。可以为注册一个回调函数,在执行完毕时使用。

【最佳价格查询器的优化

由于我们的两个远程服务:1.查询价格,2.查询折扣价格都是基于网络的。所以很容易出现某一个商店的数据迟迟无法返回的情况。由于这些原因,我希望查询器在查询时能够将拿到数据先返回过来,而不是等待所有的异步查询完成后集中返回一个List。
我们首要需要避免的就是:等待创建一个包含所有价格的List。我们应该直接处理CompletableFuture流,然后去响应他的completion事件,每一个CompletableFuture对象完成时获取到相应的返回值。

先将Discount的折扣服务延迟时间修改为随机值:

    //计算折扣价格
    private static Double apply(double price ,Code code){
        //模拟远程操作的延迟
        delay();
        return (price * (100 - code.percantage)) / 100;
    }
    private static void delay(){
        try {
            //随机延迟时间
            int delay = 500 + random.nextInt(2000);
            Thread.sleep(delay);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

开始实现最佳价格查询器:

package BestPriceFinder;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 最佳价格查询器
 */
public class BestFinder {

    List shops = Arrays.asList(
            new Shop("A"),
            new Shop("B"),
            new Shop("C"),
            new Shop("D"),
            new Shop("E"),
            new Shop("F"),
            new Shop("G"),
            new Shop("H"),
            new Shop("I"),
            new Shop("J")
    );

    public void findPricesContinue(String product){
        long st = System.currentTimeMillis();
        Stream> futurePrices = shops.stream()
                //首先异步获取价格
                .map(shop -> CompletableFuture.supplyAsync(() -> shop.getPriceFormat(product),myExecutor))
                //将获取的字符串解析成对象
                .map(future -> future.thenApply(Quote::parse))
                //使用另一个异步任务有获取折扣价格
                .map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote),myExecutor)));
        //thenAccept()会在CompletableFuture完成之后使用他的返回值,这里会持续执行子线程
        CompletableFuture[] futures = futurePrices.map(f -> f.thenAccept(s -> {
                                                                            String sout = String.format("%s done in %s mesc",s,(System.currentTimeMillis() - st));
                                                                            System.out.println(sout);
                                                                         }))
                                                  .toArray(size -> new CompletableFuture[size]);
        //allOf()工厂方法接受由CompletableFuture对象构成的数组,这里使用其等待所有的子线程执行完毕
        CompletableFuture.allOf(futures).join();
    }

  

    /**
     * 异步查询
     * 相比并行流的话CompletableFuture更有优势:可以对执行器配置,设置线程池大小
     */
    @SuppressWarnings("all")
    private final Executor myExecutor = Executors.newFixedThreadPool(Math.min(shops.size(), 100), new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            //使用守护线程保证不会阻止程序的关停
            t.setDaemon(true);
            return t;
        }
    });
   

thenAccept():提供了在CompletableFuture对象完成后使用他的返回值的功能。这样我们的每一个CompletableFuture完成后就会打印他的返回值,最终等待所有的子线程完毕。

allOf():工厂方法接受由CompletableFuture对象构成的数组,数组中所有的CompletableFuture完成后它返回一个CompletableFuture对象。

anyOf():厂方法接受由CompletableFuture对象构成的数组,返回数组中第一个完成的CompletableFuture的返回值CompletableFuture对象。

【小结

执行耗时的操作,尤其是依赖了远程服务的操作,应该使用异步任务提高效率。

尽可能的提供异步API,使用CompletableFuture类提供的特性可以轻松实现。

CompletableFuture类提供了异常机制,可以管理与抛出异步任务中执行的异常。

同步的API封装到CompletableFuture中可以以异步方式使用其结果。

异步的API可以通过CompletableFuture进行合并,无论他们之间是否有依赖关系。

可以为CompletableFuture注册一个回调函数,在执行完毕时使用。

可以决定什么时候结束运行,是所有的CompletableFuture结束后,还是第一个CompletableFuture结束时就完成。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/68258.html

相关文章

  • <java并发编程实战>学习

    摘要:线程封闭当访问共享的可变数据时,通常需要使用同步。如果仅在单线程内访问数据,就不要同步。这种技术成为线程封闭。栈封闭栈封闭是线程封闭的一种特例,在栈封闭中,只能通过局部变量才能访问对象。,对象是正确创建的。 线程封闭 当访问共享的可变数据时,通常需要使用同步。一种避免使用同步的方式就是不共享数据。如果仅在单线程内访问数据,就不要同步。这种技术成为线程封闭(Thread Confine...

    Richard_Gao 评论0 收藏0
  • 从小白程序员一路晋升为大厂高级技术专家我看过哪些书籍?(建议收藏)

    摘要:大家好,我是冰河有句话叫做投资啥都不如投资自己的回报率高。马上就十一国庆假期了,给小伙伴们分享下,从小白程序员到大厂高级技术专家我看过哪些技术类书籍。 大家好,我是...

    sf_wangchong 评论0 收藏0
  • Java多线程学习)volatile关键字

    摘要:三关键字能保证原子性吗并发编程艺术这本书上说保证但是在自增操作非原子操作上不保证,多线程编程核心艺术这本书说不保证。多线程访问关键字不会发生阻塞,而关键字可能会发生阻塞关键字能保证数据的可见性,但不能保证数据的原子性。 系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)synchroniz...

    tain335 评论0 收藏0
  • Java多线程学习(七)并发编程中一些问题

    摘要:相比与其他操作系统包括其他类系统有很多的优点,其中有一项就是,其上下文切换和模式切换的时间消耗非常少。因为多线程竞争锁时会引起上下文切换。减少线程的使用。很多编程语言中都有协程。所以如何避免死锁的产生,在我们使用并发编程时至关重要。 系列文章传送门: Java多线程学习(一)Java多线程入门 Java多线程学习(二)synchronized关键字(1) java多线程学习(二)syn...

    dingding199389 评论0 收藏0
  • Java多线程学习(七)并发编程中一些问题

    摘要:因为多线程竞争锁时会引起上下文切换。减少线程的使用。举个例子如果说服务器的带宽只有,某个资源的下载速度是,系统启动个线程下载该资源并不会导致下载速度编程,所以在并发编程时,需要考虑这些资源的限制。 最近私下做一项目,一bug几日未解决,总惶恐。一日顿悟,bug不可怕,怕的是项目不存在bug,与其惧怕,何不与其刚正面。 系列文章传送门: Java多线程学习(一)Java多线程入门 Jav...

    yimo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<