ThreadPoolExecutor(转)
让ThreadPoolExecutor的workQueue占满时自动阻塞submit()方法
转载请注明出处:http://www.codelast.com/
使用Java的ThreadPoolExecutor可以并发地执行一些任务,它的基本用法是:
(1)创建一个 ThreadPoolExecutor 对象
1
|
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(workQueueSize)); |
这里使用的构造函数是:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
/** * Creates a new <tt>ThreadPoolExecutor</tt> with the given initial * parameters and default thread factory and rejected execution handler. * It may be more convenient to use one of the {@link Executors} factory * methods instead of this general purpose constructor. * * @param corePoolSize the number of threads to keep in the * pool, even if they are idle. * @param maximumPoolSize the maximum number of threads to allow in the * pool. * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the keepAliveTime * argument. * @param workQueue the queue to use for holding tasks before they * are executed. This queue will hold only the <tt>Runnable</tt> * tasks submitted by the <tt>execute</tt> method. */ public ThreadPoolExecutor( int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this (corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); } |
简要地说明一下:
第1个参数 corePoolSize 是保留在线程池中的线程数,即使这个线程是空闲的,它也会被一直保留着;
第2个参数 maximumPoolSize 是线程池中最大允许的线程数;
最后一个参数 workQueue 用于保存执行之前的task,即task会被从 workQueue 里不断地取出来,再放到线程池里去执行。
文章来源:http://www.codelast.com/
(2)利用创建的 ThreadPoolExecutor 对象,在需要的地方不断地提交(submit)任务
1
|
executor.submit( new MyTask()); |
其中,MyTask是一个你自定义的类,例如:
1
2
3
4
5
6
|
public class MyTask implements Runnable { @Override public void run() { //TODO: } } |
我们需要在 run() 方法中写执行一个具体任务的代码。
文章来源:http://www.codelast.com/
由于 workQueue 的大小有限,当我们 submit() 任务太快的时候(也就是说,task占满了workQueue里的所有空间,此时又有新的task要被提交了),会导致无法再将新的task放到workQueue中,此时,submit() 方法会抛出异常,表明“我已经吃不消了,请你分配任务慢一点”。
这在某些应用场景下是OK的,例如,在一个抓取网页的系统(爬虫)中,提交一个抓取网页的task只是一瞬间的事,而网页抓取过程通常会是速度瓶颈,所以,如果此系统负载已经非常高了,那么我们可以放弃掉一部分URL不去抓取,也不能让系统不断地积压无数URL,导致系统最终被压垮。
但是在另一些应用场景下,这却是不能接受的,因为我们不能因为系统的处理速度慢,就丢掉我们一定必须要执行的task,因为这些task非常重要,就算是多耗一些时间,让系统慢慢处理也好,但是却不能损失一个task。总之,“等得起”,但是“丢不起”。
文章来源:http://www.codelast.com/
所以问题就来了,如何在ThreadPoolExecutor的workQueue全满的情况下,使得submit()方法能block在那里,一直等到有资源了,再继续提交task?
有好多种方法可以实现类似的效果:
「1」让ThreadPoolExecutor使用自己实现的RejectedExecutionHandler,在其中阻塞式地将task放到workQueue中
这是网上很多教程提供的一个方法。
所以我们先说一下RejectedExecutionHandler是个什么鬼,它和ThreadPoolExecutor有什么关系。
ThreadPoolExecutor可以设置一个“拒绝策略”,这是指当一个task被拒绝添加到线程池中时,采取的处理措施,例如:
1
|
executor.setRejectedExecutionHandler( new ThreadPoolExecutor.DiscardPolicy()); |
这使得当task被拒绝添加到线程池中时,ThreadPoolExecutor会采用“丢弃”策略来对待这个任务,即这个task被丢弃了。
那么,如何利用RejectedExecutionHandler来阻塞submit()?
首先要知道,submit()方法是调用了workQueue的offer()方法来塞入task,而offer()方法是非阻塞的,当workQueue已经满的时候,offer()方法会立即返回false,并不会阻塞在那里等待workQueue有空出位置,所以要让submit()阻塞,关键在于改变向workQueue添加task的行为,所以,有这样一种方法:
01
02
03
04
05
06
07
08
09
10
11
|
executor.setRejectedExecutionHandler( new RejectedExecutionHandler() { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { if (!executor.isShutdown()) { try { executor.getQueue().put(r); } catch (InterruptedException e) { } } } }); |
其中,重写的rejectedExecution()方法调用了getQueue()方法,得到了workQueue,再调用其put()方法,将task放到workQueue中,而这个put()方法是阻塞的:
1
2
3
4
|
/** * Inserts the specified element into this queue, waiting if necessary for space to become available. */ void put(E e) throws InterruptedException; |
这就达到了想要的效果:当workQueue满时,submit()一个task会导致调用我们自定义的RejectedExecutionHandler,而我们自定义的RejectedExecutionHandler会保证该task继续被尝试用阻塞式的put()到workQueue中。
文章来源:http://www.codelast.com/
尽管这种方法非常简单,但是使用它是非常不好的,原因包括但不限于:
[1] ThreadPoolExecutor的API不建议这样做
01
02
03
04
05
06
07
08
09
10
11
|
/** * Returns the task queue used by this executor. Access to the * task queue is intended primarily for debugging and monitoring. * This queue may be in active use. Retrieving the task queue * does not prevent queued tasks from executing. * * @return the task queue */ public BlockingQueue<Runnable> getQueue() { return workQueue; } |
可见,API已经说明了:getQueue()主要是用于调试和监控。
[2] 可能会导致死锁等...(未仔细研究)
文章来源:http://www.codelast.com/
「2」使用CallerRunsPolicy
1
|
executor.setRejectedExecutionHandler( new ThreadPoolExecutor.CallerRunsPolicy()); |
当使用这种拒绝策略时,如果workQueue满了,ThreadPoolExecutor就会在调用者线程(即生产者线程)中执行要提交的task——生产者线程帮消费者线程干了自己“不应该干的活”。
使用这种方法的时候,生产者线程在task被执行完之前将不再能提交新的task,除此之外貌似没有什么其他问题——除了感觉有点“怪怪的”,因为生产者线程一人饰演两个角色。
文章来源:http://www.codelast.com/
「3」使用自己重写了offer()方法的BlockingQueue
由于submit()是调用workQueue的offer()方法来添加task的,而offer()是非阻塞的,所以,如果我们自己实现一个BlockingQueue,其offer()方法是阻塞的,那么,就可以用它和ThreadPoolExecutor配合,来实现submit()方法在workQueue满时的阻塞效果了(来自StackOverflow):
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
public class LimitedQueue<E> extends LinkedBlockingQueue<E> { public LimitedQueue( int maxSize) { super (maxSize); } @Override public boolean offer(E e) { // turn offer() and add() into a blocking calls (unless interrupted) try { put(e); return true ; } catch (InterruptedException ie) { Thread.currentThread().interrupt(); } return false ; } } |
然后在构造ThreadPoolExecutor对象的时候,最后一个参数workQueue使用这个LimitedQueue类的对象即可。
ThreadPoolExecutor(转)的更多相关文章
- Android线程管理之ThreadPoolExecutor自定义线程池
前言: 上篇主要介绍了使用线程池的好处以及ExecutorService接口,然后学习了通过Executors工厂类生成满足不同需求的简单线程池,但是有时候我们需要相对复杂的线程池的时候就需要我们自己 ...
- 并发包的线程池第一篇--ThreadPoolExecutor执行逻辑
学习这个很长时间了一直没有去做个总结,现在大致总结一下并发包的线程池. 首先,任何代码都是解决问题的,线程池解决什么问题? 如果我们不用线程池,每次需要跑一个线程的时候自己new一个,会导致几个问题: ...
- ThreadPoolExecutor源码学习(1)-- 主要思路
ThreadPoolExecutor是JDK自带的并发包对于线程池的实现,从JDK1.5开始,直至我所阅读的1.6与1.7的并发包代码,从代码注释上看,均出自Doug Lea之手,从代码上看JDK1. ...
- ThreadPoolExecutor源码学习(2)-- 在thrift中的应用
thrift作为一个从底到上除去业务逻辑代码,可以生成多种语言客户端以及服务器代码,涵盖了网络,IO,进程,线程管理的框架,着实庞大,不过它层次清晰,4层每层解决不同的问题,可以按需取用,相当方便. ...
- Java 线程 — ThreadPoolExecutor
线程池 线程池处理流程 核心线程池:创建新线程执行任务,需要获取全局锁 队列:将新来的任务加入队列 线程池:大于corePoolSize,并且队列已满,小于maxPoolSize,创建新的worker ...
- java 线程池ThreadPoolExecutor 如何与 AsyncTask() 组合使用。
转载请声明出处谢谢!http://www.cnblogs.com/linguanh/ 这里主要使用Executors中的4种静态创建线程池实例方法中的 newFixedThreadPool()来举例讲 ...
- 【JUC】JDK1.8源码分析之ThreadPoolExecutor(一)
一.前言 JUC这部分还有线程池这一块没有分析,需要抓紧时间分析,下面开始ThreadPoolExecutor,其是线程池的基础,分析完了这个类会简化之后的分析,线程池可以解决两个不同问题:由于减少了 ...
- java线程池ThreadPoolExecutor使用简介
一.简介线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:ThreadPoolExecutor(int corePoolSize, int m ...
- java线程池ThreadPoolExecutor理解
Java通过Executors提供四种线程池,分别为:newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程.newFixe ...
- java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1f303192 rejected from java.util.concurrent.ThreadPoolExecutor@11f7cc04[Terminated, pool size = 0, active threads
java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@1f303192 rejec ...
随机推荐
- 1230.2——iOS准备(阅读开发者文档时的笔记)
1.程序启动的过程 .在桌面找到相应的应用的图标 点击图标 .main函数 UIApplication类Every app has exactly one instance of UIAp ...
- JavaScript禁止用户多次提交方法
[当服务器超载时,会出现提交卡顿的现象,但是用户在操作时,会不停重复点击提交,会造成服务器压力更大.所以我们需要进行限制] [1]将提交按钮禁止 <html> <head> & ...
- JDK,TomCat安装配置
JDK.Tomcat.myEclipse安装配置 准备安装包 JAVA运行环境包 JDK1.7下载地址: http://www.veryhuo.com/down/html/43205.html Jsp ...
- 【solr基础教程之一】Solr相关知识点串讲
Solr是Apache Lucene的一个子项目.Lucene为全文搜索功能提供了完备的API,但它只作为一个API库存在,而不能直接用于搜索.因此,Solr基于Lucene构建了一个完 ...
- 视差滚动(Parallax Scrolling)效果的原理和实现
视差滚动(Parallax Scrolling)是指让多层背景以不同的速度移动,形成立体的运动效果,带来非常出色的视觉体验.作为今年网页设计的热点趋势,越来越多的网站应用了这项技术. 一.什么是视差滚 ...
- [C++程序设计]多维数组元素的地址
设有一个二维数组a,它有3行4列.它的定义为int a[3][4]={{1,3,5,7},{9,11,13,15},{17,18,21,23}};a是一个数组名.a数组包含3行,即3个元 素:a[0] ...
- android R 文件 丢失的处理 如何重新生成
很多时候我们会遇到工程中的R.java文件丢失,必要急,修复很简单. 方法:右击你的工程(项目)——>Android Tools——>Fix Project Properties 即可. ...
- ViewBag、ViewData和TempData使用方法、区别与联系
一.区别与联系 ViewData 和 TempData 都可以传递弱类型数据,区别如下:TempData 只在当前 Action 中有效,生命周期和 View 相同:保存在Session中,Contr ...
- 如何在WPF程序中使用ArcGIS Engine的控件
原文 http://www.gisall.com/html/47/122747-4038.html WPF(Windows Presentation Foundation)是美国微软公司推出.NET ...
- grep搜索当前目录和递归搜索子目录中文本文件的特定pattern
一般在windows上文本编辑器notepad++,UE这些都有这些功能,Linux下就换了一种方式,用grep来完成文件中信息查找的方式. grep -R -n --include="*. ...