1、线程池的七个参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
1)coolPoolSize 线程池核心线程大小
线程池中维护的一个最小的线程数量,即使这些线程是空闲的,也不会销毁这些线程。除非设置了allowCoreThreadTimeOut 这个参数;
2)maximumPoolSize 线程池最大线程数量
一个任务被提交到线程池后,首先会看看线程池中有没有空闲的线程,如果有则直接将任务交给空闲的线程执行,如果没有则会放到工作队列中,如果队列满了就创建一个新线程,并从工作队列头部获取一个任务来执行,新的任务就防止到工作队列的尾部。但是如果线程池不能没有限制的创建新线程,创建线程的最大数量就是 maximumPoolSize 。
3)keepAliveTime 空闲线程存活时间
当一个线程没有任务处理 而且线程数量还大于核心线程数 此时超过这个keepAliveTime 后这个空闲线程被销毁。
4) unit 空闲线程存活时间单位 这个就是keepAliveTIme 存活的时间单位
5)workQueue 工作队列 当小于核心线程数的时候先去线程;当大于核心线程数据的时候任务被提交之后 会进入队列中 等待任务调度 队列一共有四种
⑴ ArrayBlockingQueue 基于数组的有界队列 按照先进先出的原则排序。新任务进入后直接放到队列的队尾,有界的数组可以防止资源耗尽的问题。当线程池中线程数量达到coolPoolSize 之后,再有新任务进入的话 就会放到队列的队尾。如果队列满了就会创建一个新线程执行任务,如果线程超过maximumPoolSize 这时候汇之星拒绝策略;
⑵ LinkedBlockingQuene 基于链表的无界阻塞队列(其实最大容量为Interger.MAX),按照先进先出排序。由于该队列的近似无界性,当线程池中线程数量达到corePoolSize后,再有新任务进来,会一直存入该队列,而不会去创建新线程直到maxPoolSize,因此使用该工作队列时,参数maxPoolSize其实是不起作用的
⑶ SynchronousQuene 一个不缓存任务的阻塞队列,生产者放入一个任务必须等到消费者取出这个任务。也就是说新任务进来时,不会缓存,而是直接被调度执行该任务,如果没有可用线程,则创建新线程,如果线程数量达到maxPoolSize,则执行拒绝策略;也就是说如果选择这种队列的时候可以执行的任务数量就是当前线程的数量;
⑷ PriorityBlockingQueue 具有优先级的无界阻塞队列,优先级通过参数Comparator实现
6)threadFactory 线程工厂 创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等
7)handler 拒绝策略
当工作队列中的任务已到达最大限制,并且线程池中的线程数量(maximumPoolSize)也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的,jdk中提供了4中拒绝策略
⑴ CallerRunsPolicy 该策略下,在调用者线程中直接执行被拒绝任务的run方法,除非线程池已经shutdown,则直接抛弃任务;
⑵ AbortPolicy 该策略下,直接丢弃任务,并抛出RejectedExecutionException异常。
⑶ DiscardPolicy 该策略下,直接丢弃任务,什么都不做
⑷ DiscardOldestPolicy 该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列 除非线程池已经shutdown,则直接抛弃任务;
2、线程池线程提交后的执行顺序
向线程池提交任务后,先判断线程池目前的线程数是否大于核心线程数 如果不大于就创建一个核心线程来执行任务
如果大于核心线程之后,就会判断缓冲队列是否满了,如果没有满则直接放入队列,等线程空闲的时候执行任务。
如果队列已经满了,则判读是否达到线程池设置的最大线程数,如果没有达到就执行新线程来执行任务。
如果已经达到了最大的线程数,则直接执行拒绝策略。
也有例外情况 在dubbo中,有一种线程池叫EagerThreadPoolExecutor线程池
这种线程池是 核心线程池不够了 就先启用新线程 之后达到最大线程数之后 再有新任务就放到队列中;
3、线程池选择
1). 高并发,执行耗时短的任务 ; 线程少 满足并发就好 举例子:并发是100 线程池可以设置为10 可以减少线程切换和管理的时间
2). 低并发,耗时长的任务 ;建议线程多,要保证有空闲的线程可以接受新任务,举例
3). 高并发,耗时长的任务 1。要分析任务类型,2、增加排队,3、加大线程数 尽量的将这种任务转换为高并发低耗时 使用一步或者回调 或者低并发高耗时的 可以使用负载均衡将高并发转换为若干低并发 然后可以了 ;
4、Java 创建线程池的四种方式 以及为什么不建议用Executors 创建线程
newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。线程池是无限大,当执行第二个任务的时候如果第一个任务已经完成直接复用第一个任务的线程;
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
为什么不建议使用?
1. Executors.newCachedThreadPool 创建的核心线程池和最大线程数未制定传入的执行数量,允许创建线程的数量是Integer.MAX_VALUE; 假如同时有10000个任务,假如任务执行时间大于空闲保持时间那就要创建10000个线程去处理任务,此时就会非常消耗CPU资源;
2.Executors.newFixedThreadPool(1); 允许请求队列长度未Interger.MAX_VALUE;假如任务量大的时候,绝大多数任务都会堆在队列中,非常的消耗内存,很容易OOM
3.Executors.newSingleThreadExecutor(); 允许请求队列长度未Interger.MAX_VALUE;假如任务量大的时候,绝大多数任务都会堆在队列中,非常的消耗内存,很容易OOM
4、Executors.newScheduledThreadPool(5); 允许创建线程的数量是Integer.MAX_VALUE; 假如同时有10000个任务,假如任务执行时间大于空闲保持时间那就要创建10000个线程去处理任务,此时就会非常消耗CPU资源;
参考链接:https://blog.csdn.net/ye17186/article/details/89467919
https://blog.csdn.net/qq_42794038/article/details/112795486