设为首页收藏本站
网站公告 | 这是第一条公告
     

 找回密码
 立即注册
缓存时间23 现在时间23 缓存数据 成交那只是一个开始,在成交之后建立一个恒久的关系,你就永远都是我的。晚安!

成交那只是一个开始,在成交之后建立一个恒久的关系,你就永远都是我的。晚安!

查看: 338|回复: 1

Java并行执行任务的几种方案小结

[复制链接]

  离线 

TA的专栏

  • 打卡等级:热心大叔
  • 打卡总天数:232
  • 打卡月天数:0
  • 打卡总奖励:3906
  • 最近打卡:2025-03-31 05:10:17
等级头衔

等級:晓枫资讯-上等兵

在线时间
4 小时

积分成就
威望
0
贡献
461
主题
409
精华
0
金钱
5249
积分
937
注册时间
2023-1-6
最后登录
2025-3-31

发表于 2025-3-14 19:36:40 | 显示全部楼层 |阅读模式

背景

最近在排查生产环境问题,发现商品详情接口时不时会报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()

java并行执行任务demo

在一个方法中同时调用多个方法或者服务,并等待所有结果返回

package com.test.demo;
import org.junit.Test;
import java.util.concurrent.CompletableFuture;
public class TestFuture {
    @Test
    public void testA(){
        CompletableFuture future3 = CompletableFuture.supplyAsync(() -> c());
        CompletableFuture future1 = CompletableFuture.supplyAsync(() -> a());
        CompletableFuture future2 = CompletableFuture.supplyAsync(() -> b());
        try {
            //获取并行执行任务结果
            System.out.println(future3.get());
            System.out.println(future1.get());
            System.out.println(future2.get());
        }catch (Exception e){
        }
    }
    public String a(){
        try {
            Thread.sleep(1000);
        }catch (Exception e){
        }
        return "a";
    }
    private String b(){
        try {
            //模拟业务执行时间
            Thread.sleep(2000);
        }catch (Exception e){
        }
        return "b";
    }
    private String c(){
        try {
            //模拟业务执行时间
            Thread.sleep(5000);
        }catch (Exception e){
        }
        return "c";
    }
}

测试结果:

1.png

从执行结果中可以看到一共耗时5s,如果同步进行执行,耗时应该在8s

以上为个人经验,希望能给大家一个参考,也希望大家多多支持晓枫资讯。


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
晓枫资讯-科技资讯社区-免责声明
免责声明:以上内容为本网站转自其它媒体,相关信息仅为传递更多信息之目的,不代表本网观点,亦不代表本网站赞同其观点或证实其内容的真实性。
      1、注册用户在本社区发表、转载的任何作品仅代表其个人观点,不代表本社区认同其观点。
      2、管理员及版主有权在不事先通知或不经作者准许的情况下删除其在本社区所发表的文章。
      3、本社区的文章部分内容可能来源于网络,仅供大家学习与参考,如有侵权,举报反馈:点击这里给我发消息进行删除处理。
      4、本社区一切资源不代表本站立场,并不代表本站赞同其观点和对其真实性负责。
      5、以上声明内容的最终解释权归《晓枫资讯-科技资讯社区》所有。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~

  离线 

TA的专栏

等级头衔

等級:晓枫资讯-列兵

在线时间
0 小时

积分成就
威望
0
贡献
0
主题
0
精华
0
金钱
19
积分
18
注册时间
2022-12-25
最后登录
2022-12-25

发表于 2025-4-12 22:08:58 | 显示全部楼层
感谢楼主,顶。
http://bbs.yzwlo.com 晓枫资讯--游戏IT新闻资讯~~~
严禁发布广告,淫秽、色情、赌博、暴力、凶杀、恐怖、间谍及其他违反国家法律法规的内容。!晓枫资讯-社区
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

1楼
2楼

手机版|晓枫资讯--科技资讯社区 本站已运行

CopyRight © 2022-2025 晓枫资讯--科技资讯社区 ( BBS.yzwlo.com ) . All Rights Reserved .

晓枫资讯--科技资讯社区

本站内容由用户自主分享和转载自互联网,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。

如有侵权、违反国家法律政策行为,请联系我们,我们会第一时间及时清除和处理! 举报反馈邮箱:点击这里给我发消息

Powered by Discuz! X3.5

快速回复 返回顶部 返回列表