首页
技术小册
AIGC
面试刷题
技术文章
MAGENTO
云计算
视频课程
源码下载
PDF书籍
「涨薪秘籍」
登录
注册
01 | 可见性、原子性和有序性问题:并发编程Bug的源头
02 | Java内存模型:看Java如何解决可见性和有序性问题
03 | 互斥锁(上):解决原子性问题
04 | 互斥锁(下):如何用一把锁保护多个资源?
05 | 一不小心就死锁了,怎么办?
06 | 用“等待-通知”机制优化循环等待
07 | 安全性、活跃性以及性能问题
08 | 管程:并发编程的万能钥匙
09 | Java线程(上):Java线程的生命周期
10 | Java线程(中):创建多少线程才是合适的?
11 | Java线程(下):为什么局部变量是线程安全的?
12 | 如何用面向对象思想写好并发程序?
13 | 理论基础模块热点问题答疑
14 | Lock和Condition(上):隐藏在并发包中的管程
15 | Lock和Condition(下):Dubbo如何用管程实现异步转同步?
16 | Semaphore:如何快速实现一个限流器?
17 | ReadWriteLock:如何快速实现一个完备的缓存?
18 | StampedLock:有没有比读写锁更快的锁?
19 | CountDownLatch和CyclicBarrier:如何让多线程步调一致?
20 | 并发容器:都有哪些“坑”需要我们填?
21 | 原子类:无锁工具类的典范
22 | Executor与线程池:如何创建正确的线程池?
23 | Future:如何用多线程实现最优的“烧水泡茶”程序?
24 | CompletableFuture:异步编程没那么难
25 | CompletionService:如何批量执行异步任务?
26 | Fork/Join:单机版的MapReduce
27 | 并发工具类模块热点问题答疑
28 | Immutability模式:如何利用不变性解决并发问题?
29 | Copy-on-Write模式:不是延时策略的COW
30 | 线程本地存储模式:没有共享,就没有伤害
31 | Guarded Suspension模式:等待唤醒机制的规范实现
32 | Balking模式:再谈线程安全的单例模式
33 | Thread-Per-Message模式:最简单实用的分工方法
34 | Worker Thread模式:如何避免重复创建线程?
35 | 两阶段终止模式:如何优雅地终止线程?
36 | 生产者-消费者模式:用流水线思想提高效率
37 | 设计模式模块热点问题答疑
38 | 案例分析(一):高性能限流器Guava RateLimiter
39 | 案例分析(二):高性能网络应用框架Netty
40 | 案例分析(三):高性能队列Disruptor
41 | 案例分析(四):高性能数据库连接池HiKariCP
42 | Actor模型:面向对象原生的并发模型
43 | 软件事务内存:借鉴数据库的并发经验
44 | 协程:更轻量级的线程
45 | CSP模型:Golang的主力队员
当前位置:
首页>>
技术小册>>
Java并发编程实战
小册名称:Java并发编程实战
### 第25章 CompletionService:如何批量执行异步任务? 在Java并发编程的广阔天地中,`CompletionService`是一个极为强大的工具,它提供了一种灵活的方式来批量处理异步任务,并能在任务完成时立即获得结果。这种机制尤其适用于需要并行处理大量任务,且任务完成顺序不重要的场景。本章将深入探讨`CompletionService`的工作原理、如何使用它以及它在实际应用中的优势与局限性。 #### 25.1 引言 在传统的并发模型中,当我们需要并行执行多个任务并等待它们完成时,通常会使用线程池(如`ExecutorService`)来管理这些任务的执行。然而,当任务数量庞大且我们希望在任务完成时立即处理其结果时,传统的轮询或等待所有任务完成再统一处理的方式就显得效率低下。`CompletionService`正是为了解决这一问题而设计的,它提供了一种能够异步接收任务执行结果的机制。 #### 25.2 CompletionService概述 `CompletionService`是Java并发包`java.util.concurrent`中的一个接口,它扩展了`ExecutorService`的功能,允许生产者提交任务给服务去执行,而消费者则可以按照任务完成的顺序(而非提交的顺序)来获取结果。`CompletionService`的核心在于它将任务的提交与执行结果的获取解耦,使得我们可以更高效地处理大量异步任务。 `CompletionService`接口定义了两个主要方法: - `submit(Callable<V> task)`:提交一个`Callable`任务给`CompletionService`执行,并返回一个表示该任务的`Future`。但需要注意的是,这个`Future`对象与直接使用`ExecutorService`时返回的`Future`对象不同,它主要用于内部跟踪,通常不直接用于结果的获取。 - `take()` 和 `poll(long timeout, TimeUnit unit)`:这两个方法用于从已完成的任务队列中取出结果。`take()`方法会阻塞直到有任务完成并返回其`Future`,而`poll()`方法则可以在指定时间内尝试获取已完成的任务,如果没有则返回`null`。 #### 25.3 实现CompletionService Java并发包中提供了一个`CompletionService`的实现类——`ExecutorCompletionService`,它通过将`ExecutorService`和`BlockingQueue<Future<V>>`结合使用来实现`CompletionService`接口。`ExecutorCompletionService`内部维护了一个阻塞队列,用于存放任务执行完成后的`Future`对象。当任务完成时,`ExecutorCompletionService`会将对应的`Future`放入这个队列中,从而使得消费者可以按需获取结果。 ```java ExecutorService executor = Executors.newFixedThreadPool(10); CompletionService<String> completionService = new ExecutorCompletionService<>(executor); // 提交多个任务 for (int i = 0; i < 100; i++) { final int taskId = i; completionService.submit(() -> { // 模拟耗时任务 Thread.sleep(1000); return "Result of task " + taskId; }); } // 批量获取并处理结果 for (int i = 0; i < 100; i++) { try { Future<String> future = completionService.take(); String result = future.get(); // 阻塞直到结果准备好 System.out.println(result); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } executor.shutdown(); ``` #### 25.4 优势与应用场景 `CompletionService`的优势主要体现在以下几个方面: 1. **高效性**:通过解耦任务的提交与结果的获取,`CompletionService`允许我们即时处理已完成的任务,无需等待所有任务都完成,从而提高了系统的响应速度和吞吐量。 2. **灵活性**:消费者可以按任意顺序获取任务结果,这为处理结果提供了极大的灵活性。例如,在Web服务中,我们可以先响应那些完成得快的请求,从而提升用户体验。 3. **易用性**:`ExecutorCompletionService`作为`CompletionService`的一个具体实现,与`ExecutorService`紧密集成,使得在现有代码基础上引入`CompletionService`变得简单直接。 `CompletionService`的应用场景包括但不限于: - 批量数据处理与转换。 - 异步I/O操作,如文件读写、网络请求等。 - 复杂任务的分解与并行执行。 - 实时计算与响应系统,如股票交易系统、实时数据分析等。 #### 25.5 注意事项与最佳实践 尽管`CompletionService`功能强大且灵活,但在使用过程中也需要注意以下几点: 1. **资源管理**:确保在不再需要时正确关闭`ExecutorService`,以释放系统资源。 2. **异常处理**:在调用`Future.get()`方法时可能会抛出`ExecutionException`,表示任务执行过程中抛出了异常。此外,还需要处理`InterruptedException`,以应对线程中断的情况。 3. **结果排序**:如果任务的完成顺序对最终结果有影响,则可能需要额外的逻辑来确保结果的正确排序。但请注意,`CompletionService`本身并不保证结果的顺序。 4. **性能调优**:根据任务的特点和系统的负载情况,合理设置线程池的大小和类型,以达到最优的性能。 5. **避免阻塞**:在调用`take()`或`poll()`方法时,要注意避免长时间阻塞,特别是在高并发场景下,可以考虑使用超时机制或结合其他并发控制手段。 #### 25.6 结论 `CompletionService`是Java并发编程中一个非常有用的工具,它提供了一种高效且灵活的方式来处理批量异步任务。通过解耦任务的提交与结果的获取,`CompletionService`使得我们能够更加灵活地控制任务的执行流程,并在任务完成时立即处理其结果。在实际应用中,我们应当根据具体需求选择合适的并发工具和策略,以充分发挥Java并发编程的强大功能。
上一篇:
24 | CompletableFuture:异步编程没那么难
下一篇:
26 | Fork/Join:单机版的MapReduce
该分类下的相关小册推荐:
经典设计模式Java版
Java语言基础9-常用API和常见算法
SpringBoot合辑-初级篇
Java语言基础8-Java多线程
Java语言基础7-Java中的异常
SpringBoot合辑-高级篇
Java必知必会-Maven初级
Java语言基础12-网络编程
Java语言基础6-面向对象高级
Mybatis合辑2-Mybatis映射文件
Java语言基础5-面向对象初级
Java语言基础14-枚举和注解