现在的项目中有将学生批量加入课程的需求,于是想根据这个需求测试一下ExecutorService的效率。假设一个场景:现在有100门课,1500名学生,要求每15个人加入一门课程,不重复。

查询并拼接数据:先从mysql中查询出100门课的id,再拿出1500个学生,然后将1500个学生每15人一组,分成100组,对应100门课程,方法如下:


  1. public List<List<MessagePojo>> getGroupStudent(){
  2. List<Long> courseIds = addUserMapper.find100CourseIds();//拿出100门课的id List
  3. List<MessagePojo> students = addUserMapper.find1500students(); //拿出1500个学生 List
  4. List<List<MessagePojo>> groupStudent = new ArrayList<>();
  5. //对学生分组 没15个学生一组 供100组
  6. if(students!=null&&students.size()>0){
  7. int maxPoint = 15;
  8. int part = students.size()/maxPoint;
  9. System.out.println("共有 : "+students.size()+"条,!"+" 分为 :"+part+"批,!"+" 每批 :"+maxPoint+"个");
  10. for(int i = 1;i <=part ;i++){
  11. List<MessagePojo> temp = students.subList(maxPoint*(i-1),maxPoint*i);
  12. System.out.println(i);
  13. groupStudent.add(temp);
  14. }
  15. }
  16. for (int i=0;i<100;i++) {
  17. List<MessagePojo> temp = groupStudent.get(i);
  18. for (MessagePojo messagePojo : temp) {
  19. messagePojo.setCourseId(courseIds.get(i));
  20. }
  21. }
  22. return groupStudent;
  23. }

下面这个是不使用executorSevice的插入方法,为了体现出时间差距,总共有三个逻辑比较复杂的方法,因为涉及项目结果,这里就不贴出细则了:


  1. public int doAdd(List<List<MessagePojo>> list){
  2. for (List<MessagePojo> messagePojos : list) {
  3. if (messagePojos.size()>0) {
  4. //学生和课程关系表中加入数据
  5. addUserMapper.method1(messagePojos);
  6. //初始化学生信息到总成绩表
  7. method2(messagePojos);
  8. //spoc课程表中加入学生信息(json)
  9. List<MessagePojo> classes = method3(messagePojos.get(0).getCourseId(),"save");
  10. }
  11. }
  12. return 1;
  13. }

下面这个是使用了ExecutorService的插入方法:


  1. public int doAddByExecutor(List<List<MessagePojo>> list){
  2. ExecutorService executorService = Executors.newFixedThreadPool(list.size());
  3. try{
  4. for (List<MessagePojo> messagePojos : list) {
  5. executorService.submit(() ->{
  6. //学生和课程关系表中加入数据
  7. addUserMapper.method1(messagePojos);
  8. //初始化学生信息到总成绩表
  9. method2(messagePojos);
  10. //spoc课程表中加入学生信息(json)
  11. List<MessagePojo> classes = method3(messagePojos.get(0).getCourseId(),"save");
  12. });
  13. }
  14. }catch(Exception e){
  15. e.printStackTrace();
  16. }finally {
  17. executorService.shutdownNow();
  18. }
  19. return 1;
  20. }

首先使用Executors类的静态方法newFixedThreadPool(int size)创建了线程池,其大小为list.size(),其实这里就是100,因为将学生分成了100组。然后使用executorService的submit方法执行任务,其中传入的是一个Runnable的实例,这里使用了Lambda表达式。必须要注意的是,submit方法一定要try,否则容易造成死锁,并且执行完成之后一定要shutdown掉。

我这里写了两个简单的方法,测试了一下两种插入方法的效率:

首先是普通方法:耗时2秒左右

然后是多线程方法:耗时33毫秒。

这个数据量其实不算很大,但是用多线程来跑还是能看出来耗时差距的。试想一下,如果数据量翻十倍甚至百倍万倍的时候,效率差距就出来了。

这只是ExecutorService最简单的用法。了解一下源码,先看最重要的submit方法,其中有三个重载:

Future<?> submit(Runnable task);
<T> Future<T> submit(Runnable task, T result);
<T> Future<T> submit(Callable<T> task);

submit()方法可以接受Runnable和Callable对象,返回Future对象。

再看execute方法:

void execute(Runnable command);

execute是Executor接口的方法,而ExecutorService继承自Executor。executor方法只能接受Runnable对象,没有返回值。

再来看一下ExecutorService的关闭。

void shutdown();
List<Runnable> shutdownNow();

二者的区别在于,shoudown()方法在终止前允许执行以前提交的任务,而shoutdownNow()方法阻止等待任务启动并试图停止当前正在执行的任务。通俗一点讲,shutdown()方法只是将线程池的状态设置为SHUTDOWN状态,正在执行的任务会继续执行下去,没有被执行的则不能在执行。而shutdownNow()方法则是将线程池的状态设置为STOP,正在执行的任务则被停止,没被执行的任务则返回。举一个很经典的猴子(各个线程)吃玉米(任务)的例子,假如猴子们正在非常开心的吃玉米,突然接到shutdown指令,那么猴子们则会把手上的玉米吃完,没有拿到手里的地上的玉米则不能再吃。而如果接到shutdownNow指令,这些猴子们立即停止吃玉米,手上的玉米也要放下,地上的更不能吃。(当然,假设猴子是听话的)

一般情况下,分两个阶段来关闭ExecutorService。首先调用shutdown()方法拒绝传入新任务。其次,如果有必要的话再调用shutdownNow方法取消所有遗留的任务。

上面讲到了,submit方法可以接受Runnable和Callable,这里讲一下这两者之间的区别。

我们都知道Runnbale接口只有一个run()方法,并且没有返回值。比如上面插入学生的操作,Lambda中写的代码并不关心返回值,所以这里使用Runnable就可以。但是如果我们需要关注任务线程的返回结果,就要使用Callable了,比如下面这段代码:


  1. TaskWork taskwork = new TaskWork();
  2. Future<RetureData> future = executorService.submit(taskwork);

其中TaskWork类实现了Callable接口,重写call方法。


  1. public class TaskWork implements Callable<ReturnData>{
  2. @Override
  3. public ReturnData call(){
  4. ReturnData returnData = new ReturnData();
  5. //todo
  6. return returnData;
  7. }
  8. }

Callable接口有一个泛型,实现时可以随意指定,并且call方法的返回值就是该泛型。在executorService.submit(taskwork)之后,会返回Future的实例,其中的泛型和Callable中的一致。然后通过Future的get方法可以获取返回的具体对象:

RetureData returnData = future.get();

原文地址:https://blog.csdn.net/hz_940611/article/details/81119764

ExecutorService小试牛刀的更多相关文章

  1. Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用(后续)

    在[Xamarin+Prism小试牛刀:定制跨平台Outlook邮箱应用]里面提到了Microsoft 身份认证,其实这也是一大块需要注意的地方,特作为后续补充这些知识点.上章是使用了Microsof ...

  2. Android线程管理之ExecutorService线程池

    前言: 上篇学习了线程Thread的使用,今天来学习一下线程池ExecutorService. 线程管理相关文章地址: Android线程管理之Thread使用总结 Android线程管理之Execu ...

  3. ExecutorService线程池

    ExecutorService 建立多线程的步骤: 1.定义线程类 class Handler implements Runnable{} 2.建立ExecutorService线程池 Executo ...

  4. ExecutorService中submit()和execute()的区别

    在使用java.util.concurrent下关于线程池一些类的时候,相信很多人和我一样,总是分不清submit()和execute()的区别,今天从源码方面分析总结一下. 通常,我们通过Execu ...

  5. ExecutorService与ThreadPoolTaskExecutor

    1.ExecutorService private static ExecutorService exec = null; public static ExecutorService getExecu ...

  6. Java批处理ExecutorService/CompletionService

    服务端接收一个请求,常常需要同时进行几个计算或者向其他服务发送请求,最后拼装结果返回上游.本文就来看下JDK提供几个并行处理方案,牵涉到ExcecutorService/CompletionServi ...

  7. ExecutorService线程池应用

    //线程数量 int threadNum = lists.size(); //创建一个线程池 ExecutorService pool = Executors.newFixedThreadPool(t ...

  8. Handler+ExecutorService(线程池)+MessageQueue模式+缓存模式

    android线程池的理解,晚上在家无事 预习了一下android异步加载的例子,也学习到了一个很重要的东东 那就是线程池+缓存  下面看他们的理解.[size=1.8em]Handler+Runna ...

  9. 线程池ExecutorService

    说到java开发,免不了跟多线程打交道.Executor框架便是Java 5中引入的,其内部使用了线程池机制,它在java.util.cocurrent 包下,通过该框架来控制线程的启动.执行和关闭, ...

随机推荐

  1. 微信小程序之threejs全景

    最近在开发小程序,身心疲惫,原因是功能和app相同,我裂开了. 各种封装组件,各种写页面,不过有个好处是以前写的h5拿来改一下标签,基本上还是ok的,就剩下最后几个功能,其中就有一个VR全景功能. 移 ...

  2. Java 类与类之间的调用

    方法1. 新建一个类. 然后在调用类中先进行被调用类实例化,然后通过实例化的对象访问. 例如: //先定义一个类 import static java.lang.System.out; public ...

  3. python 数据组合

  4. Leetcode34.Find First and Last Position of Element in Sorted Array在排序数组中查找元素的位置

    给定一个按照升序排列的整数数组 nums,和一个目标值 target.找出给定目标值在数组中的开始位置和结束位置. 你的算法时间复杂度必须是 O(log n) 级别. 如果数组中不存在目标值,返回 [ ...

  5. Axure之添加点击页面

    添加悬停字体变色的效果 页面载入时的频道预设(我做错了,英文版本不知道那个是页面载入时的事件) 我的博客不够完善,看不到全部的图片.我后续会修改我的网站的

  6. 大咖手把手教您,DLA一键建仓!

    DLA很早之前就支持了对关系型数据库的查询,但是一直以来用户会有一个担心: 直接分析RDS里面的数据会不会影响线上业务. 这个担心很合理,除非你要查询的RDS是专门用来做后台数据分析使用的,否则直接大 ...

  7. PHP 学习1.4

    1.session and cookie 示列: <?phpsession_start();?><!DOCTYPE html PUBLIC "-//W3C//DTD XHT ...

  8. javascript函数式编程和链式优化

    1.函数式编程理解 函数式编程可以理解为,以函数作为主要载体的编程方式,用函数去拆解.抽象一般的表达式 与命令式相比,这样做的好处在哪?主要有以下几点: (1)语义更加清晰 (2)可复用性更高 (3) ...

  9. 从0开始学习 GitHub 系列之「03.Git 速成」

    前面的 GitHub 系列文章介绍过,GitHub 是基于 Git 的,所以也就意味着 Git 是基础,如果你不会 Git ,那么接下来你完全继续不下去,所以今天的教程就来说说 Git ,当然关于 G ...

  10. Hibernate:**not found while looking for property: id https://blog.csdn.net/weixin_43827144/article/details/88935334

    https://blog.csdn.net/weixin_43827144/article/details/88935334 在程序执行时可能会报找不到属性的错误:例如:class Student n ...