CompletionService小技巧

在上一篇blogs中,我详细的解释了CompletionService的使用方法和ExecutorCompletionService的详细实现,这篇blogs中,我就介绍使用它的一个小技巧,算是对上一篇blogs的一个补完。在开始之前我们先回顾一下它的实现。

首先,在初始化ExecutorCompletionService的时候我们需要传入一个Executor,作为ExecutorCompletionService执行任务的容器。

public ExecutorCompletionService(Executor executor) { [......]}public ExecutorCompletionService(Executor executor, BlockingQueue<Future<V>> completionQueue) { [......]}

然后,调用submit方法,向它提交任务。submit方法会将我们提交的任务包装成一个QueueingFuture并提交给Executor来执行。

public Future<V> submit(Callable<V> task) { if (task == null) throw new NullPointerException(); RunnableFuture<V> f = newTaskFor(task); executor.execute(new QueueingFuture(f)); return f; }

接着,QueueingFuture会在任务执行完成后把执行结果放到队列中。

private class QueueingFuture extends FutureTask<Void> { QueueingFuture(RunnableFuture<V> task) { super(task, null); this.task = task; } protected void done() { completionQueue.add(task); } private final Future<V> task;}

最后,我们通过take或者poll方法就能拿到任务执行的结果。

下面让我们设想一个场景,我需要从网络上下载几张图片和视频并最后把它们渲染到页面上去,由于下载图片和视频都比较耗时,所以我希望能以多线程的形式进行下载。但是由于资源有限,下载的并发度不能太大,所以需要限制线程池的并发线程大小。但如果将可用线程数平均分给下载图片和下载视频的线程池,当某线程池的所有任务执行完成后,另外一个线程池也无法获取到它所释放的资源。那怎么办呢?我们可以创建一个统一的线程池,然后把两个CompletionService绑定上去,让CompletionService作为一个句柄来使用。

final ExecutorService pool = Executors.newFixedThreadPool(5);final ExecutorCompletionService<Image> imageCompletionService = new ExecutorCompletionService<>(pool);for (final String site : imageSites) { completionService.submit(new Callable<Image>() { @Override public String call() throws Exception { return IOUtils.toString(new URL("http://" + site), StandardCharsets.UTF_8); } });}final ExecutorCompletionService<Video> vidoeCompletionService = new ExecutorCompletionService<>(pool);for (final String site : videoSites) { completionService.submit(new Callable<Video>() { @Override public String call() throws Exception { return IOUtils.toString(new URL("http://" + site), StandardCharsets.UTF_8); } });}List<Image> images = new ArrayList<>();for(int i = 0; i < topSites.size(); ++i) { final Future<String> future = completionService.take(); try { images.add(future.get()); } catch (ExecutionException e) { log.warn("Error while downloading", e.getCause()); }}List<Video> videos = new ArrayList<>();for(int i = 0; i < topSites.size(); ++i) { final Future<String> future = completionService.take(); try { videos.add(future.get()); } catch (ExecutionException e) { log.warn("Error while downloading", e.getCause()); }}// ... do process content

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏lzj_learn_note

ThreadPoolExecutor学习笔记

Java有两个线程池类:ThreadPoolExecutor和ScheduledThreadPoolExecutor,且均继承于ExecutorService。...

1.3K6
来自专栏Android机动车

线程池之小结

多线程:解决多任务同时执行的需求,合理使用CPU资源。多线程的运行是根据CPU切换完成,如何切换由CPU决定,因此多线程运行具有不确定性。

783
来自专栏移动开发的那些事儿

从源码的角度分析ThreadPoolExecutor实现原理

下面继续分析线程池如何管理运行线程,其实就一句话,维护一个线程队列,然后对这个线程队列进行存取操作

1072
来自专栏编程

浅析Windows下堆的结构

*本文原创作者:hellowuzekai,本文属FreeBuf原创奖励计划,未经许可禁止转载 简介 Windows下的堆主要有两种,进程的默认堆和自己创建的私有...

27710
来自专栏Java 源码分析

Exectors框架 源码分析

Exectors框架 源码分析 1. 在阅读源码时做了大量的注释,并且做了一些测试分析源码内的执行流程,由于博客篇幅有限,并且代码阅读起来没有 IDE 方便,...

2847
来自专栏java 成神之路

ExecutorCompletionService 源码分析

2708
来自专栏好好学java的技术栈

Java多线程面试准备:聊聊Executor框架

在HotSpot VM的线程模型中,Java线程被一对一映射为本地操作系统线程。Java线程启动时会创建一个本地操作系统线程;当Java线程终止时,这个操作系统...

3145
来自专栏二进制文集

JDK源码分析 线程池

对于JDK源码分析的文章,仅仅记录我认为重要的地方。源码的细节实在太多,不可能面面俱到地写清每个逻辑。所以我的JDK源码分析,着重在JDK的体系架构层面,具体源...

1064
来自专栏Java3y

线程池你真不来了解一下吗?

2466
来自专栏FreeBuf

浅析Windows下堆的结构

简介 Windows下的堆主要有两种,进程的默认堆和自己创建的私有堆。在程序启动时,系统在刚刚创建的进程虚拟地址空间中创建一个进程的默认堆,而且程序也可以通过 ...

2328

扫码关注云+社区

领取腾讯云代金券