Java 线程池ThreadPoolExecutor
来源:互联网 发布:windows忘记开机密码 编辑:程序博客网 时间:2024/04/27 22:06
Java线程池ThreadPoolExecutor.
JDK1.5开始关于多线程加了很多特性。如:
ConcurrentHashMap:放弃使用公用锁同步每一个方法,使用了更细化的锁机制,分离锁。对于大数据量的HashMap同步操作效率有了较大提升。
CopyOnWriteArrayList:是同步List的一个并发替代品。其线程安全性来源于这样一个事实:只要有效的不可变对象被正确发布,那么访问它将不再需要更多的同步。在每次需要修改时它们会创建并重新发布一个信的容器拷贝,以此来实现可变性。
增加了Callable和Future。Callable是runnable的一个可选替代。我们之前用的Runnable是不能返回状态的,而Callable是可以返回状态,返回的状态保存在泛型Future<T>里。
JDK1.5里面还包含了一个重要的特性就是线程池。通过查看代码可以看出主要都是由大师DougLea来完成的。本文主要介绍线程池ThreadPoolExecutor的使用。
JDK1.5的线程池由Executor框架提供。Executor框架将处理请求任务的提交和它的执行解耦。可以制定执行策略。在线程池中执行线程可以重用已经存在的线程,而不是创建新的线程,可以在处理多请求时抵消线程创建、消亡产生的开销。如果线程池过大,会导致内存的高使用量,还可能耗尽资源。如果过小,会由于存在很多的处理器资源未工作,对吞吐量造成损失。
由于内容较多没有一一研究,只看了较常用的ThreadPoolExecutor,所以在这里做个介绍。ThreadPoolExecutor的继承关系如下。
Executor->ExecutorService->AbstractExecutorService->ThreadPoolExecutor
核心池大小(corepool size)、最大池的大小(maximumpool size)、存活时间(keep-alivetime)共同管理着线程的创建和销毁。
线程池类为java.util.concurrent.ThreadPoolExecutor,常用构造方法为:
/**
*Createsanew<tt>ThreadPoolExecutor</tt>withthegiveninitial
*parameters.
*
*@paramcorePoolSizethenumberofthreadstokeepinthe
*pool,eveniftheyareidle.
*@parammaximumPoolSizethemaximumnumberofthreadstoallowinthe
*pool.
*@paramkeepAliveTimewhenthenumberofthreadsisgreaterthan
*thecore,thisisthemaximumtimethatexcessidlethreads
*willwaitfornewtasksbeforeterminating.
*@paramunitthetimeunitforthekeepAliveTime
*argument.
*@paramworkQueuethequeuetouseforholdingtasksbeforethey
*areexecuted.Thisqueuewillholdonlythe<tt>Runnable</tt>
*taskssubmittedbythe<tt>execute</tt>method.
*@paramhandlerthehandlertousewhenexecutionisblocked
*becausethethreadboundsandqueuecapacitiesarereached.
*@throwsIllegalArgumentExceptionifcorePoolSize,or
*keepAliveTimelessthanzero,orifmaximumPoolSizelessthanor
*equaltozero,orifcorePoolSizegreaterthanmaximumPoolSize.
*@throwsNullPointerExceptionif<tt>workQueue</tt>
*or<tt>handler</tt>arenull.
*/
publicThreadPoolExecutor(intcorePoolSize,
intmaximumPoolSize,
longkeepAliveTime,
TimeUnitunit,
BlockingQueue<Runnable>workQueue,
RejectedExecutionHandlerhandler){
this(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,
Executors.defaultThreadFactory(),handler);
}
corePoolSize:线程池维护线程的最少数量,哪怕是空闲的。
maximumPoolSize:线程池维护线程的最大数量。
keepAliveTime:线程池维护线程所允许的空闲时间。
unit:线程池维护线程所允许的空闲时间的单位。
workQueue:线程池所使用的缓冲队列,改缓冲队列的长度决定了能够缓冲的最大数量。
拒绝任务:拒绝任务是指当线程池里面的线程数量达到maximumPoolSize且workQueue队列已满的情况下被尝试添加进来的任务。
handler:线程池对拒绝任务的处理策略。在ThreadPoolExecutor里面定义了4种handler策略,分别是
1. CallerRunsPolicy:这个策略重试添加当前的任务,他会自动重复调用execute()方法,直到成功。
2. AbortPolicy:对拒绝任务抛弃处理,并且抛出异常。
3. DiscardPolicy:对拒绝任务直接无声抛弃,没有异常信息。
4. DiscardOldestPolicy:对拒绝任务不抛弃,而是抛弃队列里面等待最久的一个线程,然后把拒绝任务加到队列。
一个任务通过execute(Runnable)方法被添加到线程池,任务就是一个Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法。
当一个任务通过execute(Runnable)方法欲添加到线程池时,线程池采用的策略如下:
1.如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2.如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列。
3.如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4.如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。
处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
理解了上面关于ThreadPoolExecutord的介绍,应该就基本能了解它的一些使用,不过在ThreadPoolExocutor里面有个关键的Worker类,所有的线程都要经过Worker的包装。这样才能够做到线程可以复用而无需重新创建线程。
同时Executors类里面有newFixedThreadPool(),newCachedThreadPool()等几个方法,实际上也是间接调用了ThreadPoolExocutor,不过是传的不同的构造参数。
下面通过一个例子的执行结果来理解
代码:
- import java.io.Serializable;
- 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 consumeTaskSleepTime = 2000;
- private static int produceTaskMaxNumber = 9;
- public static void main(String[] args) {
- // 构造一个线程池
- ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 3, 3, TimeUnit. SECONDS ,
- new ArrayBlockingQueue<Runnable>(2), 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();
- }
- }
- }
- public static class ThreadPoolTask implements Runnable, Serializable {
- private static final long serialVersionUID = 0;
- // 保存任务所需要的数据
- private Object threadPoolTaskData ;
- ThreadPoolTask(Object tasks) {
- this . threadPoolTaskData = tasks;
- }
- public void run() {
- // 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
- System. out .println( "start .." + threadPoolTaskData );
- try {
- // // 便于观察,等待一段时间
- Thread. sleep ( consumeTaskSleepTime );
- } catch (Exception e) {
- e.printStackTrace();
- }
- threadPoolTaskData = null ;
- }
- public Object getTask() {
- return this . threadPoolTaskData ;
- }
- }
- }
上面代码定义了一个corePoolSize为2,maximumPoolSize为3,workerQuene容量为3的线程池,也就是说在饱和状态下,只能容纳6个线程,3个是运行状态,3个在队列里面。同时代码又往线程池里面添加了9个线程,每个线程会运行2秒,这样必然会到达饱和状态。而饱和状态就涉及到对拒绝任务的处理策略,本处采用了ThreadPoolExecutor.DiscardOldestPolicy()的运行结果如下:
puttask@ 1
start..task@ 1
puttask@ 2
start..task@ 2
puttask@ 3
puttask@ 4
puttask@ 5
start..task@ 3
puttask@ 6
puttask@ 7
puttask@ 8
puttask@ 9
start..task@ 8
start..task@ 9
采用ThreadPoolExecutor.DiscardOldestPolicy()的运行结果如下:
puttask@ 1
start..task@ 1
puttask@ 2
start..task@ 2
puttask@ 3
puttask@ 4
puttask@ 5
start..task@ 3
puttask@ 6
start..task@ 6
start..task@ 4
start..task@ 5
puttask@ 7
start..task@ 7
puttask@ 8
puttask@ 9
start..task@ 8
start..task@ 9
采用ThreadPoolExecutor.AbortPolicy()的运行结果如下:
puttask@ 1
start..task@ 1
puttask@ 2
start..task@ 2
puttask@ 3
puttask@ 4
puttask@ 5
start..task@ 3
puttask@ 6
java.util.concurrent.RejectedExecutionException
atjava.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1477)
atjava.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:384)
atjava.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:867)
atTestThreadPool.main(TestThreadPool.java:22)
puttask@ 7
java.util.concurrent.RejectedExecutionException
atjava.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1477)
atjava.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:384)
atjava.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:867)
atTestThreadPool.main(TestThreadPool.java:22)
puttask@ 8
java.util.concurrent.RejectedExecutionException
atjava.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1477)
atjava.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:384)
atjava.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:867)
atTestThreadPool.main(TestThreadPool.java:22)
puttask@ 9
java.util.concurrent.RejectedExecutionException
atjava.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:1477)
atjava.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:384)
atjava.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:867)
atTestThreadPool.main(TestThreadPool.java:22)
start..task@ 4
start..task@ 5
采用ThreadPoolExecutor.DiscardPolicy()的运行结果如下:
puttask@ 1
start..task@ 1
puttask@ 2
start..task@ 2
puttask@ 3
puttask@ 4
puttask@ 5
start..task@ 3
puttask@ 6
puttask@ 7
puttask@ 8
puttask@ 9
start..task@ 4
start..task@ 5
从运行结果可以看出不同的Handler策略对拒绝任务的处理方式。
目前还只偏重在使用层面的理解,底层细节的原理还有待日后学习,欢迎交流。
本文部分内容和例子参考了:http://blog.csdn.net/imicro/archive/2007/08/29/1763955.aspx
- Java 线程池ThreadPoolExecutor
- Java 线程池ThreadPoolExecutor
- Java 线程池ThreadPoolExecutor
- JAVA线程池:ThreadPoolExecutor
- JAVA线程池ThreadPoolExecutor
- JAVA线程池ThreadPoolExecutor
- ThreadPoolExecutor java 线程池
- Java 线程池ThreadPoolExecutor
- java 线程池ThreadPoolExecutor
- JAVA线程池ThreadPoolExecutor
- java线程池:ThreadPoolExecutor
- Java线程池ThreadPoolExecutor
- Java 线程池 ThreadPoolExecutor
- java线程池ThreadPoolExecutor
- Java线程池ThreadPoolExecutor
- java 线程池ThreadPoolExecutor
- Java线程池ThreadPoolExecutor
- JAVA ThreadPoolExecutor线程池
- validate方法与validate*的应用
- ant 个人笔记
- PHP-字符处理-字符串过滤
- hadoop学习随记 二
- frameset跳转
- Java 线程池ThreadPoolExecutor
- PS2 读电脑键盘实验
- 王爽汇编第二版检测点15.1(整理)
- java 概念
- android学习(一)——基础配置
- 如何在Html里显示"<"和">"
- CUDA
- #pragma once 与 #ifndef 区别
- java中类的生命周期