版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

本文链接:https://blog.csdn.net/micro_hz/article/details/73865016

Java在语言层面提供了多线程的支持,线程池能够避免频繁的线程创建和销毁的开销,因此很多时候在项目当中我们是使用的线程池去完成多线程的任务。
Java提供了Executors 框架提供了一些基础的组件能够轻松的完成多线程异步的操作,Executors提供了一系列的静态工厂方法能够获取不同的ExecutorService实现,ExecutorService扩展了Executors接口,Executors相当简单:

public interface Executor {void execute(Runnable command);
}

把任务本身和任务的执行解耦了,如果说Runnable是可异步执行任务的抽象,那Executor就是如何执行可异步执行任务的抽象,说起来比较绕口。
本文不讲解线程的一些基础知识,因为网上的其他文章已经写的足够详细和泛滥。我写写多个异步任务的并发执行与结果的获取问题。假设这样一个场景:我们要组装一个对象,这个对象由大量小的内容组成,这些内容是无关联无依赖关系的,如果我们串行的去执行,如果每个任务耗时10秒钟,一共有10个任务,那我们就需要100秒才能获取到结果。显然我们可以采用线程池,每个任务起一个线程,忽略线程启动时间,我们只需要10秒钟就能获取到结果。这里还有两种选择,这10秒钟我们可以去做其他事,也可以等待结果。
我们来完成这样的操作:

// 这是任务的抽象
class GetContentTask implements Callable<String> {
  private String name;private Integer sleepTimes;public GetContentTask(String name, Integer sleepTimes) {this.name = name;this.sleepTimes = sleepTimes;}public String call() throws Exception {// 假设这是一个比较耗时的操作Thread.sleep(sleepTimes * 1000);return "this is content : hello " + this.name;}}

采用completionService :

// 方法一ExecutorService executorService = Executors.newCachedThreadPool();CompletionService<String> completionService = new ExecutorCompletionService(executorService);ExecuteServiceDemo executeServiceDemo = new ExecuteServiceDemo();// 十个long startTime = System.currentTimeMillis();int count = 0;for (int i = 0;i < 10;i ++) {count ++;GetContentTask getContentTask = new ExecuteServiceDemo.GetContentTask("micro" + i, 10);completionService.submit(getContentTask);}System.out.println("提交完任务,主线程空闲了, 可以去做一些事情。");// 假装做了8秒种其他事情try {Thread.sleep(8000);System.out.println("主线程做完了,等待结果");} catch (InterruptedException e) {e.printStackTrace();}try {// 做完事情要结果for (int i = 0;i < count;i ++) {Future<String> result = completionService.take();System.out.println(result.get());}long endTime = System.currentTimeMillis();System.out.println("耗时 : " + (endTime - startTime) / 1000);}  catch (Exception ex) {System.out.println(ex.getMessage());}

执行结果为:

提交完任务,主线程空闲了, 可以去做一些事情。
主线程做完了,等待结果
this is content : hello micro9
this is content : hello micro7
this is content : hello micro2
this is content : hello micro5
this is content : hello micro4
this is content : hello micro8
this is content : hello micro1
this is content : hello micro3
this is content : hello micro0
this is content : hello micro6
耗时 : 10

如果多个不想一个一个提交,可以采用 invokeAll一并提交,但是会同步等待这些任务

// 方法二ExecutorService executeService = Executors.newCachedThreadPool();List<GetContentTask> taskList = new ArrayList<GetContentTask>();long startTime = System.currentTimeMillis();for (int i = 0;i < 10;i ++) {taskList.add(new GetContentTask("micro" + i, 10));}try {System.out.println("主线程发起异步任务请求");List<Future<String>> resultList = executeService.invokeAll(taskList);// 这里会阻塞等待resultList获取到所有异步执行的结果才会执行 for (Future<String> future : resultList) {System.out.println(future.get());}// 主线程假装很忙执行8秒钟Thread.sleep(8);long endTime = System.currentTimeMillis();System.out.println("耗时 : " + (endTime - startTime) / 1000);} catch (Exception e) {e.printStackTrace();}
主线程发起异步任务请求
this is content : hello micro0
this is content : hello micro1
this is content : hello micro2
this is content : hello micro3
this is content : hello micro4
this is content : hello micro5
this is content : hello micro6
this is content : hello micro7
this is content : hello micro8
this is content : hello micro9
耗时 : 10

如果一系列请求,我们并不需要等待每个请求,我们可以invokeAny,只要某一个请求返回即可。

ExecutorService executorService = Executors.newCachedThreadPool();ArrayList<GetContentTask> taskList = new ArrayList<GetContentTask>();taskList.add(new GetContentTask("micro1",3));taskList.add(new GetContentTask("micro2", 6));try {List<Future<String>> resultList = executorService.invokeAll(taskList);// 等待6秒
//          String result2 = executorService.invokeAny(taskList); // 等待3秒// invokeAll 提交一堆任务并行处理并拿到结果// invokeAny就是提交一堆并行任务拿到一个结果即可for (Future<String> result : resultList) {System.out.println(result.get());}
//          System.out.println(result2);} catch (Exception e) {e.printStackTrace();}System.out.println("主线程等待");

如果我虽然发送了一堆异步的任务,但是我只等待一定的时间,在规定的时间没有返回我就不要了,例如很多时候的网络请求其他服务器如果要数据,由于网络原因不能一直等待,在规定时间内去拿,拿不到就我使用一个默认值。这样的场景,我们可以使用下面的写法:

try {ExecutorService executorService = Executors.newCachedThreadPool();List<Callable<String>> taskList = new ArrayList<Callable<String>>();taskList.add(new GetContentTask("micro1", 4));taskList.add(new GetContentTask("micro2", 6));// 等待五秒List<Future<String>> resultList = executorService.invokeAll(taskList, 5, TimeUnit.SECONDS);for (Future<String> future : resultList) {System.out.println(future.get());}} catch (Exception e) {e.printStackTrace();}
this is content : hello micro1
java.util.concurrent.CancellationExceptionat java.util.concurrent.FutureTask.report(FutureTask.java:121)at java.util.concurrent.FutureTask.get(FutureTask.java:192)at com.micro.demo.spring.ExecuteServiceDemo.main(ExecuteServiceDemo.java:105)

因为只等待5秒,6秒的那个任务自然获取不到,抛出异常,如果讲等待时间设置成8秒,就都能获取到。

看了此篇文章是不是感觉收获蛮大

                                </div><link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-095d4a0b23.css" rel="stylesheet"></div>

Java线程池并发执行多个任务相关推荐

  1. java线程池并发_Java并发教程–线程池

    java线程池并发 Java 1.5中提供的最通用的并发增强功能之一是引入了可自定义的线程池. 这些线程池使您可以对诸如线程数,线程重用,调度和线程构造之类的东西进行大量控制. 让我们回顾一下. 首先 ...

  2. java线程池延期执行一次_Java使用者的延期执行

    java线程池延期执行一次 在前面的博客文章(" 延迟执行Java的供应商 "),我引用礁HORSTMANN的陈述书中' 的Java SE8为真的很急关于lambda表达式','所 ...

  3. JAVA线程池_并发队列工作笔记0004---Callable原理_多线程执行Callable任务

    技术交流QQ群[JAVA,C++,Python,.NET,BigData,AI]:170933152 注意之前咱们说的线程池队列中都是用的实现了Runnbale接口的线程, 这里咱们用的是实现了Cal ...

  4. java线程池拒绝策略_Java核心知识 多线程并发 线程池原理(二十三)

    线程池做的工作主要是控制运行的线程的数量,处理过程中将任务放入队列,然后在线程创建后 启动这些任务,如果线程数量超过了最大数量超出数量的线程排队等候,等其它线程执行完毕, 再从队列中取出任务来执行.他 ...

  5. 聊聊并发(三)——JAVA线程池的分析和使用

    1. 引言 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要等到线程创建就能立即执行.第三:提 ...

  6. java线程池_Java多线程并发:线程基本方法+线程池原理+阻塞队列原理技术分享...

    线程基本方法有哪些? 线程相关的基本方法有 wait,notify,notifyAll,sleep,join,yield 等. 线程等待(wait) 调用该方法的线程进入 WAITING 状态,只有等 ...

  7. java 线程池 -- (Java并发)

    池技术是作为一个架构师必须深刻理解的东西,比如线程池,连接池,对象池,内存池等. 首先需要问一个问题:在c/c++ 编程中,你是如何操作一个任务的或者给一个线程添加任务的?如果你很清楚,那么你知道Ja ...

  8. java线程池_Java 并发编程 线程池源码实战

    作者 | 马启航 杏仁后端工程师.「我头发还多,你们呢?」 一.概述 笔者在网上看了好多的关于线程池原理.源码分析相关的文章,但是说实话,没有一篇让我觉得读完之后豁然开朗,完完全全的明白线程池,要么写 ...

  9. JAVA线程池ScheduledExecutorService周期性地执行任务 与单个Thread周期性执行任务的异常处理...

    本文记录: 1,使用ScheduledExecutorService的 scheduleAtFixedRate 方法执行周期性任务的过程,讨论了在任务周期执行过程中出现了异常,会导致周期任务失败. 2 ...

最新文章

  1. Docker安装Apache与运行简单的web服务——httpd helloworld
  2. linux的strace命令
  3. vsftpd的不同安装方式及服务控制脚本
  4. 微信公众平台开发(97) 图文消息
  5. Lesson 31-32 Personal Habits
  6. mark:apache建站permission问题
  7. 《Science》日本科学家利用干细胞诱导成功了大鼠生殖细胞
  8. Oracle Database Gateway 安装
  9. 安徽省农商行计算机类考试,2017安徽农商行备考:计算机的系统组成
  10. css不继承上级样式_CSS基础知识(一)
  11. React 组件开发 传参(详解)。
  12. 如何打开.hdx文件
  13. MATLAB LSTM多输入单输出 模式分类 示例解析(含代码)
  14. 8.PMAC上位机-VC编程环境配置
  15. iPS细胞技术难点以及iPS相关实验材料
  16. 美团java后端_美团笔试题(Java后端5题2小时)(示例代码)
  17. Unity - Timeline 之 Timeline window(Timeline窗口)
  18. 网络与信息安全工程师职位要求
  19. 各大主流BBS论坛程序简介
  20. 分布式理论 PACELC 了解么?

热门文章

  1. “钩子”、体验和思考人生,一家瑞典游戏工作室如何站稳脚跟,又不被自己的野心呛到
  2. e语言做爱奇艺视频采集_现有的15种奇异(和疯狂)编程语言
  3. 数据中心温度云图三维可视化
  4. ps 套索工具抠图实例
  5. jquery-实现的添加个人信息加验证,附完全的注释,相信大家可以看懂
  6. 从春招到秋招,算法工程师养成记
  7. (深入篇)漫游语音识别技术—带你走进语音识别技术的世界
  8. 使用idea解决包依赖冲突的问题SLF4J: Actual binding is of type [org.apache.logging.slf4j.Log4jLoggerFactory
  9. 什么是做空和做多以及什么是做空期权波动率
  10. C/C++编程学习 - 第19周 ③ 不与最大数相同的数字之和