线程池java.util.concurrent.ThreadPoolExecutor总结
?
?
线程池还具有提高系统性能的优点,因为创建线程和清除线程的开销比较大。?
有两种不同类型的线程池:一是固定线程数量的线程池;二是可变数量的线程池。?
?
对于固定数量的线程池,可以使用Executors的静态方法 newFixedThreadPool 来创建 ExecutorService;或者利用 newSingleThreadPool来创建。?
? ? ? 而 ExecutorService 实现了 Executor 接口,这个接口中有一个方法:Execute(Runnable command),也就是执行线程。?
? ? ? 对于固定数量的线程池而言,如果需要执行的线程数量多于构造的数量,那么只能并发构造时的数量,剩下的线程就进入线程池的等待队列。?
? ? ? 如果不需要使用该线程池了,则使用 ExecutorService 中的 shutDown 方法,此时,该线程池就不会接受执行新的线程任务了。
?
对于可变数量的线程池,可用Executors的静态方法 newCachedThreadPool 来创建 ExecutorService,该线程池的大小是不定的,当执行任务时,会先选取缓存中的空闲线程来执行,如果没有空闲线程,则创建一个新的线程,而如果空闲线程的空闲状态超过60秒,则线程池删除该线程。
?
还有一种线程池:延迟线程池?
该线程池的创建有两个方法: Executors.newScheduledThreadPool(int corePoolSize);
??????????????????????????????????????? Executors.newSingleScheduledExecutor();?
创建之后,会获得一个 ScheduledExecutorService。?
该对象的一个重要的方法就是: schedule(Runnable command, long delay, TimeUnit unit)
?该方法返回了一个 ScheduledFuture。
?
?
?
JDK1.5中加入了许多对并发特性的支持,例如:线程池。
一、简介
线程池类为java.util.concurrent.ThreadPoolExecutor,常用构造方法为:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,long keepAliveTime, TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler)
?
corePoolSize:??????? 线程池维护线程的最少数量 (core : 核心)
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime:????线程池维护线程所允许的空闲时间
unit:?????????? 线程池维护线程所允许的空闲时间的单位
workQueue:线程池所使用的缓冲队列
handler:????? 线程池对拒绝任务的处理策略
一个任务通过execute(Runnable)方法被添加到线程池,任务就是一个 Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果线程池中运行的线程小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果线程池中运行的线程大于等于corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满(即无法将请求加入队列),并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。
当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
也就是:处理任务的优先级为:
corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。workQueue常用的是: java.util.concurrent.ArrayBlockingQueuehandler有四个选择:ThreadPoolExecutor.AbortPolicy() //抛出java.util.concurrent.RejectedExecutionException异常ThreadPoolExecutor.CallerRunsPolicy() //重试添加当前的任务,他会自动重复调用execute()方法ThreadPoolExecutor.DiscardOldestPolicy() //抛弃旧的任务ThreadPoolExecutor.DiscardPolicy() // 抛弃当前的任务
一个例子:?
package test;import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class TestThreadPool {private static int produceTaskSleepTime = 2;private static int produceTaskMaxNumber = 10;/** * 线程池执行的任务 */public static class ThreadPoolTask implements Runnable{// 保存任务所需要的数据private Object threadPoolTaskData;ThreadPoolTask(Object tasks) {this.threadPoolTaskData = tasks;}public void run() {// 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句System.out.println("start .." + threadPoolTaskData);try {//便于观察,等待一段时间Thread.sleep(2000);} catch (Exception e) {e.printStackTrace();}threadPoolTaskData = null;}public Object getTask() {return this.threadPoolTaskData;}}public static void main(String[] args) {// 构造一个线程池ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3,TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3),new ThreadPoolExecutor.DiscardOldestPolicy());for (int i = 1; i <= produceTaskMaxNumber; i++) {try {// 产生一个任务,并将其加入到线程池String task = "task@ " + i;System.out.println("put " + task);threadPool.execute(new ThreadPoolTask(task));// 便于观察,等待一段时间Thread.sleep(produceTaskSleepTime);} catch (Exception e) {e.printStackTrace();}}}}?
说明:
1、在这段程序中,一个任务就是一个Runnable类型的对象,也就是一个ThreadPoolTask类型的对象。
2、一般来说任务除了处理方式外,还需要处理的数据,处理的数据通过构造方法传给任务。
3、在这段程序中,main()方法相当于一个残忍的领导,他派发出许多任务,丢给一个叫 threadPool的任劳任怨的小组来做。
4、这个小组里面队员至少有两个,如果他们两个忙不过来,任务就被放到任务列表里面。
如果积压的任务过多,多到任务列表都装不下(超过3个)的时候,就雇佣新的队员来帮忙。但是基于成本的考虑,不能雇佣太多的队员,至多只能雇佣 4个。
5、如果四个队员都在忙时,再有新的任务,这个小组就处理不了了,任务就会被通过一种策略来处理,我们的处理方式是不停的派发,直到接受这个任务为止(更残忍!呵呵)。
因为队员工作是需要成本的,如果工作很闲,闲到3 秒都没有新的任务了,那么有的队员就会被解雇了,但是,为了小组的正常运转,即使工作再闲,小组的队员也不能少于两个。
本例来源:http://blog.csdn.net/senton/article/details/3528720
?
Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。
线程池的类体系结构:
ExecutorService: ??? ??? 真正的线程池接口。
ScheduledExecutorService ??? 能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。
ThreadPoolExecutor ??? ??? ExecutorService的默认实现。
ScheduledThreadPoolExecutor ??? 继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。
?
Executors 工厂方法:
newSingleThreadExecutor: //单个后台线程 (注意其缓冲队列是无界的,想想为什么)创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。newFixedThreadPool: //固定大小线程池 (其缓冲队列是无界的)创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。newCachedThreadPool: //无界线程池,可以进行自动线程回收创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。newScheduledThreadPool: //创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
ThreadPoolExecutor是Executors类的底层实现:
ExecutorService newFixedThreadPool(int nThreads) 固定大小线程池
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
?corePoolSize和maximumPoolSize的大小是一样的(实际上,如果使用无界queue的话maximumPoolSize参数是没有意义的),BlockingQueue选择了LinkedBlockingQueue,该queue有一个特点,他是无界的。
一个例子:
public class ThreadPool {private static void doSomething(int id) { System.out.println("start do " + id + " task …"); try { Thread.sleep(1000 * 2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("start do " + id + " finished.");}public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(2);//创建了一个线程池,里面有2个线程//通过submit()方法,提交一个Runnable的实例,这个实例将交由线程池中空闲的线程执行。 executorService.submit(new Runnable() { public void run() { doSomething(1); } }); executorService.execute(new Runnable() { public void run() { doSomething(2); } }); executorService.shutdown(); System.out.println(">>main thread end."); }}
?
ExecutorService newSingleThreadExecutor() 单线程
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
?最大和最小都为1
?
ExecutorService newCachedThreadPool() 无界线程池
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
这个实现就有意思了。首先是无界的线程池,所以我们可以发现maximumPoolSize为big big。其次BlockingQueue的选择上使用SynchronousQueue
。可能对于该BlockingQueue有些陌生,简单说:该QUEUE中,每个插入操作必须等待另一个
线程的对应移除操作。比如,我先添加一个元素,接下来如果继续想尝试添加则会阻塞,直到另一个线程取走一个元素,反之亦然。(想到什么?就是缓冲区为1的生产者消费者模式^_^)
?
以下为重要分析:
到此如果有很多疑问,那是必然了(除非你也很了解了)
?
先从BlockingQueue<Runnable>?workQueue这个入参开始说起。在JDK中,其实已经说得很清楚了,一共有三种类型的queue。以下为引用:(我会稍微修改一下,并用红色突出显示)
??
new ThreadPoolExecutor( 2, 3, 30, TimeUnit.SECONDS, new <span style="white-space: normal;">SynchronousQueue</span><Runnable>(), new RecorderThreadFactory("CookieRecorderPool"), new ThreadPoolExecutor.CallerRunsPolicy()); ?new ThreadPoolExecutor(2, 3, 30, TimeUnit.SECONDS, new SynchronousQueue<Runnable>(), new RecorderThreadFactory("CookieRecorderPool"), new ThreadPoolExecutor.CallerRunsPolicy());
?当核心线程已经有2个正在运行.
?
LinkedBlockingQueue
new ThreadPoolExecutor(2, 4, 30, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2), new RecorderThreadFactory("CookieRecorderPool"), new ThreadPoolExecutor.CallerRunsPolicy());
假设,所有的任务都永远无法执行完。
?
对于首先来的A,B来说直接运行,接下来,如果来了C,D,他们会被放到queu中,如果接下来再来E,F,则增加线程运行E,F。但是如果再来任务,队列无法再接受了,线程数也到达最大的限制了,所以就会使用拒绝策略来处理。
?
总结:
参考:http://dongxuan.iteye.com/blog/901689
http://www.cnblogs.com/jersey/archive/2011/03/30/2000231.html
?
自己创建线程池:http://sunnylocus.iteye.com/blog/223327
1 楼 悲剧了 2011-12-07 建议直接看JDK1.6 java.util.concurrent下面的文档,需要的都有