ThreadPoolExecutor线程池的创建和参数详解

来源:互联网 发布:星际皆知你爱我txt 编辑:程序博客网 时间:2024/06/17 17:54
(文章部分内容转载自:http://blog.csdn.net/zhouhl_cn/article/details/7392607,向大神致敬!)最近在做后再后台管理系统发送邮件的优化的时候需要用到线程池的实现ThreadPoolExecutor,来发送邮件,这也是JDK1.5以来,Java引入了强大的concurrent包,它给我们带来了极大的方便,但同时,对于该线程池不恰当的设置也可能使其效率并不能达到预期的效果,甚至仅相当于或低于单线程的效率。

JDK中关于ThreadPoolExecutor的源码:

public ThreadPoolExecutor(int corePoolSize,                              int maximumPoolSize,                              long keepAliveTime,                              TimeUnit unit,                              BlockingQueue<Runnable> workQueue,                              RejectedExecutionHandler handler) {        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,             Executors.defaultThreadFactory(), handler);    }
在源码中我们能看到ThreadPoolExecutor中的所有的参数,这些参数是实现线程池高效工作的关键:corePoolSize核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。maxPoolSize当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。keepAliveTime当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0。allowCoreThreadTimeout是否允许核心线程空闲退出,默认值为false。queueCapacity任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。

在实际成产中,如何实现ThreadPoolExecutor呢?它又是怎么执行的呢?

public class PoolSend {    BlockingQueue<Runnable> workQueue;//任务队列    ExecutorService executorService;//线程池接口    //构造线程方法    public PoolSend(){        workQueue = new LinkedBlockingQueue<Runnable>();//构造无界的任务队列,资源足够,理论可以支持无限个任务。        executorService = new ThreadPoolExecutor(2, 10,                 30,TimeUnit.SECONDS, workQueue,                 new ThreadPoolExecutor.CallerRunsPolicy());    }    //将任务放到线程池中    public void send(Runnable task){        System.out.println("Pool Send sending mail...");        executorService.execute(task);    }    //关闭线程池    public void close(){        executorService.shutdown();    }}
查看ThreadPoolExecutor中的execute方法可以知道:1、当线程数小于核心线程数时,创建线程。2、当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。3、当线程数大于等于核心线程数,且任务队列已满:    1、若线程数小于最大线程数,创建线程    2、若线程数等于最大线程数,抛出异常,拒绝任务

关于参数的详细设置如下
corePoolSize:

每个任务需要tasktime秒处理,则每个线程每钞可处理1/tasktime个任务。系统每秒有tasks个任务需要处理,则需要的线程数为:tasks/(1/tasktime),即tasks*tasktime个线程数。假设系统每秒任务数为100~1000,每个任务耗时0.1秒,则需要100*0.1至1000*0.1,即10~100个线程。那么corePoolSize应该设置为大于10,具体数字最好根据8020原则,即80%情况下系统每秒任务数,若系统80%的情况下第秒任务数小于200,最多时为1000,则corePoolSize可设置为20。

queueCapacity:

任务队列的长度要根据核心线程数,以及系统对任务响应时间的要求有关。队列长度可以设置为(corePoolSize/tasktime)*responsetime: (20/0.1)*2=400,即队列长度可设置为400。队列长度设置过大,会导致任务响应时间过长,切忌以下写法:LinkedBlockingQueue queue = new LinkedBlockingQueue();这实际上是将队列长度设置为Integer.MAX_VALUE,将会导致线程数量永远为corePoolSize,再也不会增加,当任务数量陡增时,任务响应时间也将随之陡增。

maxPoolSize:

当系统负载达到最大值时,核心线程数已无法按时处理完所有任务,这时就需要增加线程。每秒200个任务需要20个线程,那么当每秒达到1000个任务时,则需要(1000-queueCapacity)*(20/200),即60个线程,可将maxPoolSize设置为60。

keepAliveTime:

线程数量只增加不减少也不行。当负载降低时,可减少线程数量,如果一个线程空闲时间达到keepAliveTiime,该线程就退出。默认情况下线程池最少会保持corePoolSize个线程。

allowCoreThreadTimeout:

默认情况下核心线程不会退出,可通过将该参数设置为true,让核心线程也退出。以上关于线程数量的计算并没有考虑CPU的情况。若结合CPU的情况,比如,当线程数量达到50时,CPU达到100%,则将maxPoolSize设置为60也不合适,此时若系统负载长时间维持在每秒1000个任务,则超出线程池处理能力,应设法降低每个任务的处理时间(tasktime)。
0 0
原创粉丝点击