利用Java代码写一个并行调用模板

目录
  • 前言:
  • 1. 一个串行调用的例子
  • 2. CompletionService实现并行调用
  • 3. 抽取通用的并行调用方法
  • 4. 代码思考以及设计模式应用
  • 5. 思考总结

前言:

本文主要介绍内容有:

  • 一个串行调用的例子(App首页信息查询)
  • CompletionService实现并行调用
  • 抽取通用的并行调用方法
  • 代码思考以及设计模式应用
  • 思考总结

1. 一个串行调用的例子

如果让你设计一个APP首页查询的接口,它需要查用户信息、需要查banner信息、需要查标签信息等等。

般情况,小伙伴会实现如下:

public AppHeadInfoResponse queryAppHeadInfo(AppInfoReq req) {
    //查用户信息
    UserInfoParam userInfoParam = buildUserParam(req);
    UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam);
    //查banner信息
    BannerParam bannerParam = buildBannerParam(req);
    BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam);
    //查标签信息
    LabelParam labelParam = buildLabelParam(req);
    LabelDTO labelDTO = labelService.queryLabelInfo(labelParam);
    //组装结果
    return buildResponse(userInfoDTO,bannerDTO,labelDTO);
}

这段代码会有什么问题嘛? 其实这是一段挺正常的代码,但是这个方法实现中,查询用户、banner、标签信息,是串行的,如果查询用户信息200ms,查询banner信息100ms,查询标签信息200ms的话,耗时就是500ms啦。

其实为了优化性能,我们可以修改为并行调用的方式,耗时可以降为200ms如下图所示:

2. CompletionService实现并行调用

对于上面的例子,如何实现并行调用呢?

有小伙伴说,可以使用Future+Callable实现多个任务的并行调用。但是线程池执行批量任务时,返回值用Future的get()获取是阻塞的,如果前一个任务执行比较耗时的话,get()方法会阻塞,形成排队等待的情况。

CompletionService是对定义ExecutorService进行了包装,可以一边生成任务,一边获取任务的返回值。让这两件事分开执行,任务之间不会互相阻塞,可以获取最先完成的任务结果。

CompletionService的实现原理比较简单,底层通过FutureTask+阻塞队列,实现了任务先完成的话,可优先获取到。也就是说任务执行结果按照完成的先后顺序来排序,先完成可以优化获取到。内部有一个先进先出的阻塞队列,用于保存已经执行完成的Future,你调用CompletionService的poll或take方法即可获取到一个已经执行完成的Future,进而通过调用Future接口实现类的get方法获取最终的结果。

接下来,我们来看下,如何用CompletionService,实现并行查询APP首页信息哈。

思考步骤如下:

我们先把查询用户信息的任务,放到线程池,如下:

ExecutorService executor = Executors.newFixedThreadPool(10);
//查询用户信息
CompletionService<UserInfoDTO> userDTOCompletionService = new ExecutorCompletionService<UserInfoDTO>(executor);
Callable<UserInfoDTO> userInfoDTOCallableTask = () -> {
      UserInfoParam userInfoParam = buildUserParam(req);
      return userService.queryUserInfo(userInfoParam);
  };
userDTOCompletionService.submit(userInfoDTOCallableTask);
  • 但是如果想把查询banner信息的任务,也放到这个线程池的话,发现不好放了,因为返回类型不一样,一个是UserInfoDTO,另外一个是BannerDTO。那这时候,我们是不是把泛型声明为Object即可,因为所有对象都是继承于Object的?如下:
ExecutorService executor = Executors.newFixedThreadPool(10);
//查询用户信息
CompletionService<Object> baseDTOCompletionService = new ExecutorCompletionService<Object>(executor);
Callable<Object> userInfoDTOCallableTask = () -> {
    UserInfoParam userInfoParam = buildUserParam(req);
    return userService.queryUserInfo(userInfoParam);
};
//banner信息任务
Callable<Object> bannerDTOCallableTask = () -> {
    BannerParam bannerParam = buildBannerParam(req);
    return bannerService.queryBannerInfo(bannerParam);
};
//提交用户信息任务
baseDTOCompletionService.submit(userInfoDTOCallableTask);
//提交banner信息任务
baseDTOCompletionService.submit(bannerDTOCallableTask);
  • 这里会有个问题,就是获取返回值的时候,我们不知道哪个Object是用户信息的DTO,哪个是BannerDTO怎么办呢?这时候,我们可以在参数里面做个扩展嘛,即参数声明为一个基础对象BaseRspDTO,再搞个泛型放Object数据的,然后基础对象BaseRspDTO有个区分是UserDTO还是BannerDTO的唯一标记属性key。代码如下:
public class BaseRspDTO<T extends Object> {
    //区分是DTO返回的唯一标记,比如是UserInfoDTO还是BannerDTO
    private String key;
    //返回的data
    private T data;
    public String getKey() {
        return key;
    }
    public void setKey(String key) {
        this.key = key;
    }
    public T getData() {
        return data;
    }
    public void setData(T data) {
        this.data = data;
    }
}
//并行查询App首页信息
public AppHeadInfoResponse parallelQueryAppHeadPageInfo(AppInfoReq req) {
    long beginTime = System.currentTimeMillis();
    System.out.println("开始并行查询app首页信息,开始时间:" + beginTime);
    ExecutorService executor = Executors.newFixedThreadPool(10);
    CompletionService<BaseRspDTO<Object>> baseDTOCompletionService = new ExecutorCompletionService<BaseRspDTO<Object>>(executor);
    //查询用户信息任务
    Callable<BaseRspDTO<Object>> userInfoDTOCallableTask = () -> {
        UserInfoParam userInfoParam = buildUserParam(req);
        UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam);
        BaseRspDTO<Object> userBaseRspDTO = new BaseRspDTO<Object>();
        userBaseRspDTO.setKey("userInfoDTO");
        userBaseRspDTO.setData(userInfoDTO);
        return userBaseRspDTO;
    };
    //banner信息查询任务
    Callable<BaseRspDTO<Object>> bannerDTOCallableTask = () -> {
        BannerParam bannerParam = buildBannerParam(req);
        BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam);
        BaseRspDTO<Object> bannerBaseRspDTO = new BaseRspDTO<Object>();
        bannerBaseRspDTO.setKey("bannerDTO");
        bannerBaseRspDTO.setData(bannerDTO);
        return bannerBaseRspDTO;
    };
    //label信息查询任务
    Callable<BaseRspDTO<Object>> labelDTODTOCallableTask = () -> {
        LabelParam labelParam = buildLabelParam(req);
        LabelDTO labelDTO = labelService.queryLabelInfo(labelParam);
        BaseRspDTO<Object> labelBaseRspDTO = new BaseRspDTO<Object>();
        labelBaseRspDTO.setKey("labelDTO");
        labelBaseRspDTO.setData(labelDTO);
        return labelBaseRspDTO;
    };
    //提交用户信息任务
    baseDTOCompletionService.submit(userInfoDTOCallableTask);
    //提交banner信息任务
    baseDTOCompletionService.submit(bannerDTOCallableTask);
    //提交label信息任务
    baseDTOCompletionService.submit(labelDTODTOCallableTask);
    UserInfoDTO userInfoDTO = null;
    BannerDTO bannerDTO = null;
    LabelDTO labelDTO = null;
    try {
        //因为提交了3个任务,所以获取结果次数是3
        for (int i = 0; i < 3; i++) {
            Future<BaseRspDTO<Object>> baseRspDTOFuture = baseDTOCompletionService.poll(1, TimeUnit.SECONDS);
            BaseRspDTO baseRspDTO = baseRspDTOFuture.get();
            if ("userInfoDTO".equals(baseRspDTO.getKey())) {
                userInfoDTO = (UserInfoDTO) baseRspDTO.getData();
            } else if ("bannerDTO".equals(baseRspDTO.getKey())) {
                bannerDTO = (BannerDTO) baseRspDTO.getData();
            } else if ("labelDTO".equals(baseRspDTO.getKey())) {
                labelDTO = (LabelDTO) baseRspDTO.getData();
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }
    System.out.println("结束并行查询app首页信息,总耗时:" + (System.currentTimeMillis() - beginTime));
    return buildResponse(userInfoDTO, bannerDTO, labelDTO);
}

到这里为止,一个基于CompletionService实现并行调用的例子已经实现啦。是不是很开心,哈哈。

3. 抽取通用的并行调用方法

我们回过来观察下第2小节,查询app首页信息的demo:CompletionService实现了并行调用。大家有没有什么其他想法呢?比如,假设别的业务场景,也想通过并行调用优化,那是不是也得搞一套类似第2小节的代码。所以,我们是不是可以抽取一个通用的并行方法,让别的场景也可以用,对吧?这就是后端思维啦

基于第2小节的代码,我们如何抽取通用并行调用方法呢。

首先,这个通用的并行调用方法,不能跟业务相关的属性挂钩,对吧,所以方法的入参应该有哪些呢?

方法的入参,可以有Callable对吧。因为并行,肯定是多个Callable任务的。所以,入参应该是一个Callable的数组。再然后,基于上面的APP首页查询的例子,Callable里面得带BaseRspDTO泛型,对吧?因此入参就是List<Callable<BaseRspDTO<Object>>> list

那并行调用的出参呢? 你有多个Callable的任务,是不是得有多个对应的返回,因此,你的出参可以是List<BaseRspDTO<Object>>。我们抽取的通用并行调用模板,就可以写成酱紫:

public List<BaseRspDTO<Object>> executeTask(List<Callable<BaseRspDTO<Object>>> taskList) {
List<BaseRspDTO<Object>> resultList = new ArrayList<>();
//校验参数
 if (taskList == null || taskList.size() == 0) {
 return resultList;
        }
 ExecutorService executor = Executors.newFixedThreadPool(10);
 CompletionService<BaseRspDTO<Object>> baseDTOCompletionService = new ExecutorCompletionService<BaseRspDTO<Object>>(executor);
 //提交任务
 for (Callable<BaseRspDTO<Object>> task : taskList) {
 baseDTOCompletionService.submit(task);
        }
        try {
            //遍历获取结果
            for (int i = 0; i < taskList.size(); i++) {
                Future<BaseRspDTO<Object>> baseRspDTOFuture = baseDTOCompletionService.poll(2, TimeUnit.SECONDS);
                resultList.add(baseRspDTOFuture.get());
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
        return resultList;
    }

既然我们是抽取通用的并行调用方法,那以上的方法是否还有哪些地方需要改进的呢?

  • 第一个可以优化的地方,就是executor线程池,比如有些业务场景想用A线程池,有些业务想用B线程池,那么,这个方法,就不通用啦,对吧。我们可以把线程池以参数的实行提供出来,给调用方自己控制。
  • 第二个可以优化的地方,就是CompletionServicepoll方法获取时,超时时间是写死的。因为不同业务场景,超时时间可能不一样。所以,超时时间也是可以以参数形式放出来,给调用方自己控制。

我们再次优化一下这个通用的并行调用模板,代码如下:

public List<BaseRspDTO<Object>> executeTask(List<Callable<BaseRspDTO<Object>>> taskList, long timeOut, ExecutorService executor) {
    List<BaseRspDTO<Object>> resultList = new ArrayList<>();
    //校验参数
    if (taskList == null || taskList.size() == 0) {
        return resultList;
    }
    if (executor == null) {
        return resultList;
    }
    if (timeOut <= 0) {
        return resultList;
    }
    //提交任务
    CompletionService<BaseRspDTO<Object>> baseDTOCompletionService = new ExecutorCompletionService<BaseRspDTO<Object>>(executor);
    for (Callable<BaseRspDTO<Object>> task : taskList) {
        baseDTOCompletionService.submit(task);
    }

    try {
        //遍历获取结果
        for (int i = 0; i < taskList.size(); i++) {
          Future<BaseRspDTO<Object>> baseRspDTOFuture = baseDTOCompletionService.poll(timeOut, TimeUnit.SECONDS);
          resultList.add(baseRspDTOFuture.get());
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        e.printStackTrace();
    }

    return resultList;
}

以后别的场景也需要用到并行调用的话,直接调用你的这个方法即可,是不是有点小小的成就感啦,哈哈。

4. 代码思考以及设计模式应用

我们把抽取的那个公用的并行调用方法,应用到App首页信息查询的例子,

代码如下:

public AppHeadInfoResponse parallelQueryAppHeadPageInfo1(AppInfoReq req) {
        long beginTime = System.currentTimeMillis();
        System.out.println("开始并行查询app首页信息,开始时间:" + beginTime);
        //用户信息查询任务
        Callable<BaseRspDTO<Object>> userInfoDTOCallableTask = () -> {
            UserInfoParam userInfoParam = buildUserParam(req);
            UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam);
            BaseRspDTO<Object> userBaseRspDTO = new BaseRspDTO<Object>();
            userBaseRspDTO.setKey("userInfoDTO");
            userBaseRspDTO.setData(userInfoDTO);
            return userBaseRspDTO;
        };
        //banner信息查询任务
        Callable<BaseRspDTO<Object>> bannerDTOCallableTask = () -> {
            BannerParam bannerParam = buildBannerParam(req);
            BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam);
            BaseRspDTO<Object> bannerBaseRspDTO = new BaseRspDTO<Object>();
            bannerBaseRspDTO.setKey("bannerDTO");
            bannerBaseRspDTO.setData(bannerDTO);
            return bannerBaseRspDTO;
        };
        //label信息查询任务
        Callable<BaseRspDTO<Object>> labelDTODTOCallableTask = () -> {
            LabelParam labelParam = buildLabelParam(req);
            LabelDTO labelDTO = labelService.queryLabelInfo(labelParam);
            BaseRspDTO<Object> labelBaseRspDTO = new BaseRspDTO<Object>();
            labelBaseRspDTO.setKey("labelDTO");
            labelBaseRspDTO.setData(labelDTO);
            return labelBaseRspDTO;
        };
        List<Callable<BaseRspDTO<Object>>> taskList = new ArrayList<>();
        taskList.add(userInfoDTOCallableTask);
        taskList.add(bannerDTOCallableTask);
        taskList.add(labelDTODTOCallableTask);
        ExecutorService executor = Executors.newFixedThreadPool(10);
        List<BaseRspDTO<Object>> resultList = parallelInvokeCommonService.executeTask(taskList, 3, executor);
        if (resultList == null || resultList.size() == 0) {
            return new AppHeadInfoResponse();
        }
        UserInfoDTO userInfoDTO = null;
        BannerDTO bannerDTO = null;
        LabelDTO labelDTO = null;

        //遍历结果
        for (int i = 0; i < resultList.size(); i++) {
            BaseRspDTO baseRspDTO = resultList.get(i);
            if ("userInfoDTO".equals(baseRspDTO.getKey())) {
                userInfoDTO = (UserInfoDTO) baseRspDTO.getData();
            } else if ("bannerDTO".equals(baseRspDTO.getKey())) {
                bannerDTO = (BannerDTO) baseRspDTO.getData();
            } else if ("labelDTO".equals(baseRspDTO.getKey())) {
                labelDTO = (LabelDTO) baseRspDTO.getData();
            }
        }
        System.out.println("结束并行查询app首页信息,总耗时:" + (System.currentTimeMillis() - beginTime));
        return buildResponse(userInfoDTO, bannerDTO, labelDTO);
    }

基于以上代码,小伙伴们,是否还有其他方面的优化想法呢? 比如这几个Callable查询任务,我们是不是也可以抽取一下?让代码更加简洁。

二话不说,现在我们直接建一个BaseTaskCommand类,实现Callable接口,把查询用户信息、查询banner信息、label标签信息的查询任务放进去。

代码如下:

public class BaseTaskCommand implements Callable<BaseRspDTO<Object>> {
    private String key;
    private AppInfoReq req;
    private IUserService userService;
    private IBannerService bannerService;
    private ILabelService labelService;
    public BaseTaskCommand(String key, AppInfoReq req, IUserService userService, IBannerService bannerService, ILabelService labelService) {
        this.key = key;
        this.req = req;
        this.userService = userService;
        this.bannerService = bannerService;
        this.labelService = labelService;
    }
    @Override
    public BaseRspDTO<Object> call() throws Exception {

        if ("userInfoDTO".equals(key)) {
            UserInfoParam userInfoParam = buildUserParam(req);
            UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam);
            BaseRspDTO<Object> userBaseRspDTO = new BaseRspDTO<Object>();
            userBaseRspDTO.setKey("userInfoDTO");
            userBaseRspDTO.setData(userInfoDTO);
            return userBaseRspDTO;
        } else if ("bannerDTO".equals(key)) {
            BannerParam bannerParam = buildBannerParam(req);
            BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam);
            BaseRspDTO<Object> bannerBaseRspDTO = new BaseRspDTO<Object>();
            bannerBaseRspDTO.setKey("bannerDTO");
            bannerBaseRspDTO.setData(bannerDTO);
            return bannerBaseRspDTO;
        } else if ("labelDTO".equals(key)) {
            LabelParam labelParam = buildLabelParam(req);
            LabelDTO labelDTO = labelService.queryLabelInfo(labelParam);
            BaseRspDTO<Object> labelBaseRspDTO = new BaseRspDTO<Object>();
            labelBaseRspDTO.setKey("labelDTO");
            labelBaseRspDTO.setData(labelDTO);
            return labelBaseRspDTO;
        }

        return null;
    }

    private UserInfoParam buildUserParam(AppInfoReq req) {
        return new UserInfoParam();
    }
    private BannerParam buildBannerParam(AppInfoReq req) {
        return new BannerParam();
    }

    private LabelParam buildLabelParam(AppInfoReq req) {
        return new LabelParam();
    }
}

以上这块代码,构造函数还是有比较多的参数,并且call()方法中,有多个if...else...,如果新增一个分支(比如查询浮层信息),那又得在call方法里修改了,并且BaseTaskCommand的构造器也要修改了

大家是否有印象,多程序中出现多个if...else...时,我们就可以考虑使用策略模式+工厂模式优化。

我们声明多个策略实现类,如下:

public interface IBaseTask {
    //返回每个策略类的key,如
    String getTaskType();
    BaseRspDTO<Object> execute(AppInfoReq req);
}
//用户信息策略类
@Service
public class UserInfoStrategyTask implements IBaseTask {
    @Autowired
    private IUserService userService;
    @Override
    public String getTaskType() {
        return "userInfoDTO";
    }
    @Override
    public BaseRspDTO<Object> execute(AppInfoReq req) {
        UserInfoParam userInfoParam = userService.buildUserParam(req);
        UserInfoDTO userInfoDTO = userService.queryUserInfo(userInfoParam);
        BaseRspDTO<Object> userBaseRspDTO = new BaseRspDTO<Object>();
        userBaseRspDTO.setKey(getTaskType());
        userBaseRspDTO.setData(userBaseRspDTO);
        return userBaseRspDTO;
    }
}

/**
  * banner信息策略实现类
  **/
@Service
public class BannerStrategyTask implements IBaseTask {
    @Autowired
    private IBannerService bannerService;
    @Override
    public String getTaskType() {
        return "bannerDTO";
    }
    @Override
    public BaseRspDTO<Object> execute(AppInfoReq req) {
        BannerParam bannerParam = bannerService.buildBannerParam(req);
        BannerDTO bannerDTO = bannerService.queryBannerInfo(bannerParam);
        BaseRspDTO<Object> bannerBaseRspDTO = new BaseRspDTO<Object>();
        bannerBaseRspDTO.setKey(getTaskType());
        bannerBaseRspDTO.setData(bannerDTO);
        return bannerBaseRspDTO;
    }
}
...

然后这几个策略实现类,怎么交给spring管理呢? 我们可以实现ApplicationContextAware接口,把策略的实现类注入到一个map,然后根据请求方不同的策略请求类型(即DTO的类型),去实现不同的策略类调用。其实这类似于工厂模式的思想。

代码如下:

/**
  * 策略工厂类
  **/
@Component
public class TaskStrategyFactory implements ApplicationContextAware {
    private Map<String, IBaseTask> map = new ConcurrentHashMap<>();
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        Map<String, IBaseTask> tempMap = applicationContext.getBeansOfType(IBaseTask.class);
        tempMap.values().forEach(iBaseTask -> {
            map.put(iBaseTask.getTaskType(), iBaseTask);
        });
    }
    public BaseRspDTO<Object> executeTask(String key, AppInfoReq req) {
        IBaseTask baseTask = map.get(key);
        if (baseTask != null) {
            System.out.println("工厂策略实现类执行");
            return baseTask.execute(req);
        }
        return null;
    }
}

有了策略工厂类TaskStrategyFactory,我们再回来优化下BaseTaskCommand类的代码。它的构造器已经不需要多个IUserService userService, IBannerService bannerService, ILabelService labelService啦,只需要策略工厂类TaskStrategyFactory即可。同时策略也不需要多个if...else...判断了,用策略工厂类TaskStrategyFactory代替即可。

优化后的代码如下:

public class BaseTaskCommand implements Callable<BaseRspDTO<Object>> {
    private String key;
    private AppInfoReq req;
    private TaskStrategyFactory taskStrategyFactory;
    public BaseTaskCommand(String key, AppInfoReq req, TaskStrategyFactory taskStrategyFactory) {
        this.key = key;
        this.req = req;
        this.taskStrategyFactory = taskStrategyFactory;
    }
    @Override
    public BaseRspDTO<Object> call() throws Exception {
        return taskStrategyFactory.executeTask(key, req);
    }
}  

因此整个app首页信息并行查询,就可以优化成这样啦,如下:

public AppHeadInfoResponse parallelQueryAppHeadPageInfo2(AppInfoReq req) {
    long beginTime = System.currentTimeMillis();
    System.out.println("开始并行查询app首页信息(最终版本),开始时间:" + beginTime);
    List<Callable<BaseRspDTO<Object>>> taskList = new ArrayList<>();
    //用户信息查询任务
    taskList.add(new BaseTaskCommand("userInfoDTO", req, taskStrategyFactory));
    //banner查询任务
    taskList.add(new BaseTaskCommand("bannerDTO", req, taskStrategyFactory));
    //标签查询任务
    taskList.add(new BaseTaskCommand("labelDTO", req, taskStrategyFactory));

    ExecutorService executor = Executors.newFixedThreadPool(10);
    List<BaseRspDTO<Object>> resultList = parallelInvokeCommonService.executeTask(taskList, 3, executor);

    if (resultList == null || resultList.size() == 0) {
        return new AppHeadInfoResponse();
    }
    UserInfoDTO userInfoDTO = null;
    BannerDTO bannerDTO = null;
    LabelDTO labelDTO = null;
    for (BaseRspDTO<Object> baseRspDTO : resultList) {
        if ("userInfoDTO".equals(baseRspDTO.getKey())) {
            userInfoDTO = (UserInfoDTO) baseRspDTO.getData();
        } else if ("bannerDTO".equals(baseRspDTO.getKey())) {
            bannerDTO = (BannerDTO) baseRspDTO.getData();
        } else if ("labelDTO".equals(baseRspDTO.getKey())) {
            labelDTO = (LabelDTO) baseRspDTO.getData();
        }
    }

    System.out.println("结束并行查询app首页信息(最终版本),总耗时:" + (System.currentTimeMillis() - beginTime));
    return buildResponse(userInfoDTO, bannerDTO, labelDTO);
  }

5. 思考总结

以上代码整体优化下来,已经很简洁啦。那还有没有别的优化思路呢。

其实还是有的,比如,把唯一标记的key定义为枚举,而不是写死的字符串"userInfoDTO"、"bannerDTO","labelDTO"。还有,除了CompletionService,有些小伙伴喜欢用CompletableFuture实行并行调用。

本文大家学到了哪些知识呢?

  • 如何优化接口性能?某些场景下,可以使用并行调用代替串行。
  • 如何实现并行调用呢? 可以使用CompletionService
  • 学到的后端思维是? 日常开发中,要学会抽取通用的方法、或者工具。
  • 策略模式和工厂模式的应用

到此这篇关于利用Java代码写一个并行调用模板的文章就介绍到这了,更多相关Java并行调用模板内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • java异步调用的4种实现方法

    目录 一.利用多线程 直接new线程 使用线程池 二.采用Spring的异步方法去执行(无返回值) @Async注解可以用在方法上,也可以用在类上,用在类上,对类里面所有方法起作用 三.采用Spring的异步方法+Future接收返回值 如果调用之后接收返回值,不对返回值进行操作则为异步操作,进行操作则转为同步操作,等待对返回值操作完之后,才会继续执行主进程下面的流程 四.原生Future方法 参考 一.利用多线程 直接new线程 Thread t = new Thread(){ @Overri

  • 极为简易的Qno SmartLink IPSec VPN设定

    VPN--Virtual Private Network,是针对在互联网技术及应用不断发展中,企业及特定用户对信息交流.传递.交换的安全性.可靠性及成本上的需求,在公共互联网的基础上,通过遂道和加密技术,构建虚拟专用网的解决方案. VPN构建中的一项重要的核心任务是遂道技术,而IPSec--因特网安全协议(即遂道协议)是VPN技术中较为成熟的遂道协议.相对于PPTP.SSL这两种协议来说, IPSec虽然公认是最值得信赖和安全的VPN协议,但同时也是出名的难以设置,安装配置和管理都比较复杂,尤其

  • 如何在Java中调用python文件执行详解

    目录 一.Java内置Jpython库(不推荐) 1.1 下载与使用 1.2 缺陷 二.使用Runtime.getRuntime()执行脚本⽂件 2.1 使用 2.2 缺陷 三.利用cmd调用python文件 3.1 使用 3.2 优化 总结 一.Java内置Jpython库(不推荐) 1.1 下载与使用 可以在官网下载jar包,官网:http://ftp.cuhk.edu.hk/pub/packages/apache.org/ 或者使用maven进行jar包下载 <dependency> &

  • 在Java中按值调用和按引用调用

    目录 一.为什么按值调用和按引用调用? 二.按值调用 三.引用调用 四.为什么Java中只有按值调用,没有按引用调用? 五.Java中的引用调用,但如何调用? 六.Java对象引用 七.结论 一.为什么按值调用和按引用调用? 方法或函数可以通过两种方式调用.一种是按值调用,另一种是按引用调用,这两种方式通常根据作为输入或参数传递给它们的值的类型来区分. 在我们开始之前,让我澄清一件事,在Java中,只有按值调用(值传递),没有按引用调用. 二.按值调用 按值调用是一种方法,通过复制给定变量(或常

  • 利用Java代码写一个并行调用模板

    目录 前言: 1. 一个串行调用的例子 2. CompletionService实现并行调用 3. 抽取通用的并行调用方法 4. 代码思考以及设计模式应用 5. 思考总结 前言: 本文主要介绍内容有: 一个串行调用的例子(App首页信息查询) CompletionService实现并行调用 抽取通用的并行调用方法 代码思考以及设计模式应用 思考总结 1. 一个串行调用的例子 如果让你设计一个APP首页查询的接口,它需要查用户信息.需要查banner信息.需要查标签信息等等. 一般情况,小伙伴会实

  • 利用Java手写一个简易的lombok的示例代码

    目录 1.概述 2.lombok使用方法 3.lombok原理解析 4.手写简易lombok 1.概述 在面向对象编程中,必不可少的需要在代码中定义对象模型,而在基于Java的业务平台开发实践中尤其如此.相信大家在平时开发中也深有感触,本来是没有多少代码开发量的,但是因为定义的业务模型对象比较多,而需要重复写Getter/Setter.构造器方法.字符串输出的ToString方法.Equals/HashCode方法等.我们都知道Lombok能够替大家完成这些繁琐的操作,但是其背后的原理很少有人会

  • 如何用120行Java代码写一个自己的区块链

    区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Java 语言来实现一个简单的区块链,用不到 120 行代码来揭示区块链的原理! "用不到120行 Java 代码就能实现一个自己的区块链!" 听起来不可思议吧?有什么能比开发一个自己的区块链更好的学习实践方法呢?那我们就一起来实践下! 因为我们是一家从事互联网金融的科技公司,所以我们采用虚拟资产金额作为这篇文章中的示例数据.大家可以先为自己想一个数字,后

  • 利用Java手写阻塞队列的示例代码

    目录 前言 需求分析 阻塞队列实现原理 线程阻塞和唤醒 数组循环使用 代码实现 成员变量定义 构造函数 put函数 offer函数 add函数 take函数 重写toString函数 完整代码 总结 前言 在我们平时编程的时候一个很重要的工具就是容器,在本篇文章当中主要给大家介绍阻塞队列的原理,并且在了解原理之后自己动手实现一个低配版的阻塞队列. 需求分析 在前面的两篇文章ArrayDeque(JDK双端队列)源码深度剖析和深入剖析(JDK)ArrayQueue源码当中我们仔细介绍了队列的原理,

  • 利用Java简单实现一个代码行数统计器方法实例

    前言 哈喽,我是小黑, 最近学了java的输入输出流后一直心痒痒,总想找一点事情来做,所以用java代码来实现了一下统计代码的所有行数,看一下我上大学以来到底打了多少行. 先附上实现代码吧! package InOutStream; import java.util.* ; import java.io.* ; class codeCount { private static int count ; //统计总行数 private static int countCPP ;//CPP priva

  • 80行代码写一个Webpack插件并发布到npm

    1. 前言 最近在学习 Webpack 相关的原理,以前只知道 Webpack 的配置方法,但并不知道其内部流程,经过一轮的学习,感觉获益良多,为了巩固学习的内容,我决定尝试自己动手写一个插件. 这个插件实现的功能比较简单: 默认清除 js 代码中的 console.log 的打印输出: 可通过传入配置,实现移除 console 的其它方法,如 console.warn.console.error 等: 2. Webpack 的构建流程以及 plugin 的原理 2.1 Webpack 构建流程

  • Python利用3D引擎写一个Pong游戏

    目录 前言 实现方法 完整代码 前言 之前,我们用pygame做了一个2D的Pong游戏,今天我们做一个3D的,游戏画面如下: 用ad和←→操作,双人对战 实现该效果我们使用Python强大的3D引擎Ursina,基础的使用方法见这篇文章:详解Python 3D引擎Ursina如何绘制立体图形 接下来开始写代码吧! 实现方法 首先,导入ursina和随机库 from ursina import * import random as rd 定义两个玩家的分数 scorea=scoreb=0 然后,

  • 利用java反射机制实现自动调用类的简单方法

    1. 新建TestServlet类 package com.yanek.test; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.ht

  • 200行Java代码编写一个计算器程序

    发现了大学时候写的计算器小程序,还有个图形界面,能够图形化展示表达式语法树,哈哈;) 只有200行Java代码,不但能够计算加减乘除,还能够匹配小括号~ 代码点评: 从朴素的界面配色到简单易懂错误提示,无不体现了"用户体验"至上的设计理念:代码异常处理全面合理.滴水不漏,代码缩进优雅大方,变量命名直观易懂:再结合长度适中简单明了的注释,程序整体给人一种清新脱俗之感.背后不难看出作者对学习的热爱以及对设计的苛求,工匠精神可见一斑,真可谓是大学数据结构学以致用的典范! 实现代码如下所示:

  • 如何利用Java AWT 创建一个简易计算器

    目录 一.关于AWT 二.逻辑部分 1.对于数字按钮 2.对于算术按钮 3.对于等号按钮 4.对于清除按钮 5.对于退格按钮 6.特殊插件功能 7.==例如==: 三.GIF演示 四.附完整代码 摘要:手把手教你使用 Java AWT 创建一个简易计算器. 一.关于AWT AWT (抽象窗口工具包)是一个有助于构建 GUI 的 API (图形用户界面)基于 java 应用程序.GUI使用一些图形帮助用户交互.它主要由一组的类和方法所必需的,如在一个简化的方式创建和管理的GUI按钮,窗口,框架,文

随机推荐