zoukankan      html  css  js  c++  java
  • ExecutorService小试牛刀

    现在的项目中有将学生批量加入课程的需求,于是想根据这个需求测试一下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
  • 相关阅读:
    Atitit 集团与个人的完整入口列表 attilax的完整入口 1. 集团与个人的完整入口列表 1 2. 流量入口概念 2 3. 流量入口的历史与发展 2 1.集团与个人的完整入口列表
    atitit 每季度日程表 每季度流程 v3 qaf.docx Ver history V2 add diary cyar data 3 cate V3 fix detail 3cate ,
    Atitit react 详细使用总结 绑定列表显示 attilax总结 1. 前言 1 1.1. 资料数量在百度内的数量对比 1 1.2. 版本16 v15.6.1 1 1.3. 引入js 2
    Atitit r2017 r3 doc list on home ntpc.docx
    Atitit r2017 ra doc list on home ntpc.docx
    Atiitt attilax掌握的前后技术放在简历里面.docx
    Atitit q2016 qa doc list on home ntpc.docx
    Atitit r7 doc list on home ntpc.docx 驱动器 D 中的卷是 p2soft 卷的序列号是 9AD0D3C8 D:\ati\r2017 v3 r01\
    Atitit 可移植性之道attilax著
    Atitit q2016 q5 doc list on home ntpc.docx
  • 原文地址:https://www.cnblogs.com/jpfss/p/11142957.html
Copyright © 2011-2022 走看看