ThreadPoolExecutor(int corePoolSize, // 1
int maximumPoolSize, // 2
long keepAliveTime, // 3
TimeUnit unit, // 4
BlockingQueue<Runnable> workQueue, // 5
ThreadFactory threadFactory, // 6
RejectedExecutionHandler handler )
【corePoolSize】核心线程数,没达到核心线程数时,会创建新的线程。当达到核心线程数时,任务会进去队列
【maximumPoolSize】最大线程数,可以为Integer.MAX_VALUE 21亿。当达到核心线程数时,会去创建额外的线程来执行任务,最多不超过最大线程数
【keepAliveTime】存活时间,当任务处理完成,额外的线程存活一段时间后,会自行销毁。空闲等待时间(该参数默认对核心线程无效,当allowCoreThreadTimeOut手动设置为true时,核心线程超过存活时间后才会被销毁)
【TimeUnit】空闲等待时间的单位
【queue】利用什么队列来存放任务,有界队列、无界队列等。BlockingQueue为无界队列
【ThreadFactory】线程创建工厂
【RejectExecutionHandler】拒绝策略
假设把queue做成有界队列,如下new ArrayBlockingQueue<Runnable>(200),那么假设corePoolSize个线程都在繁忙的工作,大量的任务进入有界队列,队伍也满了,此时怎么办?
如果maximumPoolSize最大线程数比corePoolSize核心线程数大,会额外创建新的线程放入线程池中,用于处理队列中的任务。这些额外的线程处理完一个任务后,会尝试从队列中获取新的任务继续执行
如果额外的线程用完了,而且也在繁忙中,队列也是满的。这个时候还有新任务怎么办?
通过拒绝策略将任务拒绝掉,有几种拒绝策略,可以传入RejectExecutionHandler
(1)AbortPolicy
(2)DiscardPolicy
(3)DiscardOldestPolicy
(4)CallRunsPolicy
(5)自定义拒绝策略
一般比较常用的是fixedThreadPool。可根据上述原理去定制自己的线程池,考虑到核心线程数、队列类型、最大线程数、拒绝策略以及线程释放时间。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue()); }
corePoolSize与maximumPoolSize相等,即其线程全为核心线程,是一个固定大小的线程池,是其优势;
keepAliveTime = 0 该参数默认对核心线程无效,而FixedThreadPool全部为核心线程;
workQueue 为LinkedBlockingQueue(无界阻塞队列),队列最大值Integer.MAX_VALUE。如果任务提交速度持续大余任务处理速度,会造成队列大量阻塞。因为队列很大,很有可能在拒绝策略前,内存溢出。是其劣势;
FixedThreadPool的任务执行是无序的;
【评论区】
1、在实际的使用中,应该如何合理配置核心线程数和队列长度呢?
线程池数量取决于业务逻辑,计算密集型还是io密集型,队列长度取决于队列中每个对象的大小,32位的系统和64位的采取指针压缩的系统的每个对象的大小也是不一样的,然后根据你jvm的启动参数,推算得出
2、如果corePoolSize大于maximunPoolSize,最多创建的线程数以哪个为准?
抛异常,throw new IllegalArgumentException();
3、线程池能创建多个吗,我们生产中有许多任务是单独创建线程池?
完全可以 ,ThreadPoolExecutor 是 new 出来的,不是单例的
4、如果线程池使用newFixedThreadPool创建,如果阻塞队列使用的是无界队列的话,那么永远不会新建额外线程处理任务,那么额外线程岂不是没用了? 为何要有这种设计呢? 而且这种方式容易导致任务堆积,造成OOM,岂不是很危险,那么为什么不直接使用有界队列和指定数量的最大线程数这种方式,一次解决这些隐患呢?
这个要考虑具体的使用场景,有写场景下不允许拒绝丢弃任务,这时候就只能等线程池的线程慢慢的去消化队列里面的任务。如果场景允许丢弃任务那丢弃完全没问题
corePoolsize和maxmunPoolSize的意思,核心池的数量如果等于最大线程池那就是只用核心池了,最大线程池的数量包括核心池的数量呢,在ThreadPoolExecutor类的构造方法中,如果maximumPoolSize < corePoolSize的话,会抛出IllegalArgumentException异常,工厂类的线程池方法,maxmunPoolSize如果是Integer.MAX_VALUE的话,核心池的数量一般是0,来一个任务创建一个线程,IO密集型的任务才会用到这个,至于具体的创建线程池,建议最好采用构造方法手动创建,并指定有界队列