博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java并行执行任务的几种方案
阅读量:6279 次
发布时间:2019-06-22

本文共 3070 字,大约阅读时间需要 10 分钟。

背景

最近在排查生产环境问题,发现商品详情接口时不时会报RPC调用超时,检查代码发现接口里面查询活动耗时比较长,都是串行执行的,仔细查看发现完全可以改成并行去执行,缩短接口查询耗时。 比如我们的商品详情接口,需要展示立减、阶梯满减、团购等活动标签。需要查询三次不同的活动信息,再组装活动标签信息。如果每次查询耗时1s,按照串行的方式去调用,整个接口下来至少需要3s,整个耗时,对于我们来讲是无法接受的。其实在jdk中,给我们提供了几种非常便捷的并行执行任务的方法。

  • CountDownLatch

  • ExecutorService.invokeAll()

  • Fork/Join 分而治之 有点类似MapReduce的影子,这个有兴趣的可以自行去了解


改进方案

  • 代码例子:
private void assemblyActivityTag(CartItemDTO itemDTO){                    //1.查询立减活动信息,耗时1s                 //2.查询阶梯满减活动信息,耗时1s                //3.查询团购活动信息,耗时1s                //4.组装活动标签信息,耗时1s                    // 串行执行下来整个耗时4s    }复制代码
  • CountDownLatch
private void assemblyActivityTag(CartItemDTO itemDTO){        ExecutorService executorService = Executors.newCachedThreadPool();        CountDownLatch latch = new CountDownLatch(3);        executorService.execute(new Runnable() {            @Override            public void run() {            //1.查询立减活动信息                latch.countDown();            }        });        executorService.execute(new Runnable() {            @Override            public void run() {                //2.查询阶梯满减活动信息                latch.countDown();            }        });        executorService.execute(new Runnable() {            @Override            public void run() {                //3.查询团购活动信息                latch.countDown();            }        });        try {            // 一定记得加上timeout时间,防止阻塞主线程            latch.await(3000,TimeUnit.MILLISECONDS);        } catch (InterruptedException e) {            e.printStackTrace();        }        //4.等待所有子任务完成,组装活动标签信息                 //5.关闭线程池        executorService.shutdown();    }复制代码
  • ExecutorService.invokeAll()
private void assemblyActivityTag(CartItemDTO itemDTO) {        ExecutorService executorService = Executors.newCachedThreadPool();        List
> tasks = Lists.newArrayList(); tasks.add(new Callable
() { @Override public String call() throws Exception { //1.查询立减活动信息 return null; } }); tasks.add(new Callable
() { @Override public String call() throws Exception { //2.查询阶梯满减活动信息 return null; } }); tasks.add(new Callable
() { @Override public String call() throws Exception { //3.查询团购活动信息 return null; } }); try { List
> futureList = executorService.invokeAll(tasks, 3000, TimeUnit.MILLISECONDS); for (Future
future : futureList) { // 获取线程执行结果 try { String activityTag = future.get(); } catch (ExecutionException e) { e.printStackTrace(); } } } catch (InterruptedException e) { e.printStackTrace(); } //4.组装活动标签信息 //5.关闭线程池 executorService.shutdown(); }复制代码

注意点和区别

  • 在使用CountDownLatch,尽可能使用线程安全的容器去处理子线程的返回值,避免多线程情况下,出现脏数据。
  • 如果想知道每个子线程的对应的返回值,ExecutorService.invokeAll()方式,是没法区分的,只能依赖返回值的顺序去匹配。
  • 使用上面2种方式时,切记设置超时时间,防止子任务执行时间过长,阻塞主线程任务
  • 线程池用完结束,记得shutdown()

总结

鉴于作者才疏学浅,文中不足之处还望你不吝斧正,如有雷同纯属巧合

转载地址:http://qisva.baihongyu.com/

你可能感兴趣的文章
css面试题
查看>>
Vue组建通信
查看>>
用CSS画一个带阴影的三角形
查看>>
前端Vue:函数式组件
查看>>
程鑫峰:1.26特朗.普力挺美元力挽狂澜,伦敦金行情分析
查看>>
safari下video标签无法播放视频的问题
查看>>
01 iOS中UISearchBar 如何更改背景颜色,如何去掉两条黑线
查看>>
对象的继承及对象相关内容探究
查看>>
Spring: IOC容器的实现
查看>>
Serverless五大优势,成本和规模不是最重要的,这点才是
查看>>
Nginx 极简入门教程!
查看>>
iOS BLE 开发小记[4] 如何实现 CoreBluetooth 后台运行模式
查看>>
Item 23 不要在代码中使用新的原生态类型(raw type)
查看>>
为网页添加留言功能
查看>>
JavaScript—数组(17)
查看>>
Android 密钥保护和 C/S 网络传输安全理论指南
查看>>
以太坊ERC20代币合约优化版
查看>>
Why I Began
查看>>
同一台电脑上Windows 7和Ubuntu 14.04的CPU温度和GPU温度对比
查看>>
js数组的操作
查看>>