线程池的结构及原理
一, 线程池的参数:
corePoolSize: 线程池核心线程大小; 默认情况下这些线程不会被销毁,除非设置了allowCoreThreadTimeOut
maximumPoolSize: 线程池最大线程数量
keepAliveTime: 空闲线程存活时间
TimeUnit: 空闲线程存活时间及单位
workQueue: 工作队列
1, ArrayBlockingQueue: 基于数组的有界阻塞队列,按FIFO排序。
2, LinkedBlockingQuene: 基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照FIFO排序。由于该队列的近似无界性,因此使用该工作队列时,参数maxPoolSize其实是不起作用的。
3, SynchronousQuene: 一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略。
4, PriorityBlockingQueue: 具有优先级的无界阻塞队列,优先级通过参数Comparator实现。
handler: 拒绝策略(RejectedExecutionHandler)
- AbortPolicy: 直接抛异常
- CallerRunsPolicy:让调用者帮着跑这个任务
- DiscardOldestPolicy:丢弃队列里最老的那个任务,执行当前任务
- DiscardPolicy:不处理,直接扔掉
二, 线程池的处理过程:
- 先判断线程池中的核心线程们是否空闲,如果空闲,就把这个新的任务指派给某一个空闲线程去执行。如果没有空闲,并且当前线程池中的核心线程数还小于 corePoolSize,那就再创建一个核心线程。
- 如果线程池的线程数已经达到核心线程数,并且这些线程都繁忙,就把这个新来的任务放到等待队列中去。
- 如果等待队列又满了,那么查看一下当前线程数是否到达maximumPoolSize,如果还未到达,就继续创建线程。如果已经到达了,就交给RejectedExecutionHandler来决定怎么处理这个任务。
----------------------------------------------------------------------------------------------------------------
线程数设置和CPU数的关系
一般说来,大家认为线程池的大小经验值应该这样设置:(其中N为CPU的个数)
- 如果是CPU密集型应用,则线程池大小设置为N+1
- 如果是IO密集型应用,则线程池大小设置为2N+1(因为io读数据或者缓存的时候,线程等待,此时如果多开线程,能有效提高cpu利用率)
但是,IO优化中,这样的估算公式可能更适合: 最佳线程数目 = ((线程等待时间+线程CPU时间)/线程CPU时间 )* CPU数目 线程等待时间所占比例越高,需要越多线程。线程CPU时间所占比例越高,需要越少线程。