aix cpu耗时线程_多线程开发提升系统吞吐量

在分布式系统或者微服务系统中,我们经常会调用别的服务接口,但返回的时间却不可以预料的,如果我们等待,在高并发场景中,分分钟造成严重阻塞,这时候我们需要多线程来避免这个问题,下面我们来看看例子:

      准备:getPrice(Shring shop) , getDiscount(String shop) , getRate(String shop)  每个服务模拟延时,这里为了展示效果,设置为服务返回需要1秒时间

场景1 :我们需要获取同一商品,各个商店的报价 方式一:使用并行流
 public static void main(String[] args) throws InterruptedException {        List shops=new ArrayList<>();        //初始化6个商店        shops.add("商店1-");        shops.add("商店2-");        shops.add("商店3-");        shops.add("商店4-");        shops.add("商店5-");        shops.add("商店6-");        long startTime = System.currentTimeMillis();        TestCompletable1(shops);        System.out.println("耗时:"+(System.currentTimeMillis()-startTime)+"ms");    }    private static List  TestCompletable1(List shops){        return shops.stream().parallel().map(shop -> getPrice(shop))                .collect(Collectors.toList());    }    //CPU核数=6    输出:耗时:1042ms
如果使用并行流异步操作,对于6核CUP开6个线程没毛病,下面我们来看看如果商店数量超过CUP核数,
public static void main(String[] args) throws InterruptedException {        List shops=new ArrayList<>();        //初始化6个商品        shops.add("商店1-");        shops.add("商店2-");        shops.add("商店3-");        shops.add("商店4-");        shops.add("商店5-");        shops.add("商店6-");        shops.add("商店7-");        long startTime = System.currentTimeMillis();        TestCompletable1(shops);        System.out.println("耗时:"+(System.currentTimeMillis()-startTime)+"ms");    }    private static List  TestCompletable1(List shops){        return shops.stream().parallel().map(shop -> getPrice(shop))                .collect(Collectors.toList());    }    //CPU核数=6    输出:耗时:2043ms
我们可以看到,耗时多了一倍,这是为什么呢?因为并行流底层采用的是forkJoinPool,连接池默认是CPU核数,当6个商品开6个线程查询价格,6个线程可以同时执行,当需要查询第7个商店,那么同时只能执行6个线程,剩下一个线程在等待,当1秒后执行完了6个商店查询,还要花多1秒查询剩下的商店价格,也就是说,如果商店数量是1到6个,需要1秒多,7到12个,需要2秒多…… 小编对于这种场景,不喜欢用并行流,不灵活 方式二 :CompletableFuture
public static void main(String[] args) throws InterruptedException {        List shops=new ArrayList<>();        //初始化6个商品        shops.add("商店1-");        shops.add("商店2-");        shops.add("商店3-");        shops.add("商店4-");        shops.add("商店5-");        shops.add("商店6-");        shops.add("商店7-");        long startTime = System.currentTimeMillis();        TestCompletable2(shops);        System.out.println("耗时:"+(System.currentTimeMillis()-startTime)+"ms");    }  private static void TestCompletable2(List shops) {        Executor executor = Executors.newFixedThreadPool(shops.size());        List> collect = shops.stream()                .map(shop -> CompletableFuture.supplyAsync(() -> getPrice(shop), executor))         .collect(Collectors.toList());        System.out.println(collect.stream().map(CompletableFuture::join).collect(Collectors.toList()));    }    //CPU核数=6    输出:6个商店,耗时:1048ms          7个商店,耗时:1045ms   
在JAVA8后,对于对线程编程多了一个CompletableFuture,它实现了原来的Future接口,大大提高了开发效率,并且结合线程数,绝配。这里线程池初始化线程数量是商店数量,是保证每个商店都有子线程去执行,默认使用的线程池也是ForkJoinPool,这个时候需要我们自定义线程池。注意的是,不是线程越多就越好,要根据服务返回的时间,执行时间,CPU核数计算出最佳线程数, 公式:最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU核数 场景2:我们需要获取同一商品,各个商店的报价,针对商店原始报价,获取人民币实时汇率转人民币后并且进行打折后展示最终价格, 1.获取各个商店的报价,同时通过接口获取人民币汇率 2.转人民币后访问打折服务获取最终价格 显然,对于场景2,并行流已经不适用了,那么我们着重看看CompletableFuture 准备:模拟三个服务
  //获取价格    private static   String getPrice(String shop) {        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return shop + "获取价格-";    }    //获取汇率    private static  String getRate(String shop) {        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return shop + "获取汇率-";    }    //打折服务    private  static String getDiscount(String shop ) {        try {            Thread.sleep(1000);        } catch (InterruptedException e) {            e.printStackTrace();        }        return shop + "打折服务-";    }
   private static void TestCompletable2(List shops) {        Executor executor = Executors.newFixedThreadPool(shops.size());        List> collect = shops.stream()                .map(shop -> CompletableFuture.supplyAsync(() -> getPrice(shop), executor)                        .thenCombine(CompletableFuture.supplyAsync(() -> getRate(shop), executor), (str1, str2) -> str1 + str2))                .map(future -> future.thenCompose(chiStr -> CompletableFuture.supplyAsync(() -> getDiscount(chiStr), executor))).collect(Collectors.toList());        System.out.println(collect.stream().map(CompletableFuture::join).collect(Collectors.toList()));    }     public static void main(String[] args) throws InterruptedException {        List shops=new ArrayList<>();        shops.add("商店1-");        shops.add("商店2-");        shops.add("商店3-");        shops.add("商店4-");        shops.add("商店5-");        shops.add("商店6-");         shops.add("商店7-");        long startTime = System.currentTimeMillis();        TestCompletable2(shops);        System.out.println("耗时:"+(System.currentTimeMillis()-startTime)+"ms");    }耗时:6个商店,3049ms,7个商店,3051ms

2e36819fdf7ee2402ee3418740caae23.png

耗时不尽人意,三个服务共用了3秒多,吞吐量太差了。我们来看看原因,大家看看线程池的线程数量是商店的数量,例如有6个商店,也就是说同时只能有6个线程同时执行,但我们看看thenCombine,我们知道thenCombine是多线程联合执行,当开线程getPrice的时候,同时再开多一个线程去getRate,也就是说,同时需要12个线程执行,但是线程池只有6个线程,所以getPrice和getRate各花了1秒,thenCompose是串行异步执行,当getPrice和getRate线程返回结果后,在本线程里面连接一个异步操作CompletableFuture.supplyAsync(),也就是说,再用子线程去执行getDiscount服务,也花1秒,所以共花了3秒多。 下面我们把线程池调大,用100测试

754ee2485d59c7c7b7c44a70fb808b08.png

解释一下:getPrice和getRate是同时开两个线程调用,只要线程数够这两步是同时执行的,所以只花1秒,另外getDiscount服务再花1秒,共2秒。

经过主编测试,商店20个,30个或者40个,都只耗时2秒多,因为线程池是用100的,大家在开发时,要实事求是,选择适合的线程数。

思考:

1.如何等到所有线程都返回结果后,再执行下面逻辑.

2.如何做到只要有一个线程返回结果就不再等待,立刻执行下面逻辑,这种速度快,给客服体验感超棒.

有需要的可以联系小编一起探讨,因能力有限,请多多指教!


6638a058c9f1ee0318a3d993dc151fb2.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值