Java线程池七个参数详解
一、前言
1.1 线程池是什么
线程池(thread pool):线程池是一种池化技术,类似的有数据库连接池,HTTP连接池、IP池等。
池化管理线程,无需额外创建和销毁线程,能避免创建过多线程导致线程频繁调度从而减低性能
1.2 线程池有什么用
总的来说有4点好处:
-
降低资源消耗:通过重复利用现有的线程来执行任务,避免多次创建和销毁线程。 -
提高相应速度:因为省去了创建线程这个步骤,所以在任务来的时候,可以立刻开始执行。 -
提高线程的可管理性:线程池进行统一的分配、调优和监控。 -
提供更多更强大的功能:线程池的可拓展性使得我们可以自己加入新的功能,比如说定时、延时来执行某些线程。
二、线程池的设计与实现
2.1 总体设计
Java中线程池核心实现类是ThreadPoolExecutor,首先看下ThreadPoolExecutor的UML类图,了解ThreadPoolExecutor的继承关系。
ThreadPoolExecutor的顶层接口是Executor:将任务提交和任务执行进行解耦。用户无需关注如何创建线程,如何调度线程来执行任务,用户只需要提供Runnable对象,将任务运行逻辑提交到执行器(Executor)中,由Executor框架来调配和执行。
2.2 ThreadPoolExecutor的运行逻辑
当任务提交到线程池里之后,需要经过以下流程
这里设corePoolSize(核心线程数量)为10,maximumPoolSize (最大线程数量)为20
三、线程池的7个核心参数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler);
3.1 corePoolSize 线程池核心线程大小
线程池中维护的一个最少的线程数量,即使这些线程处于空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。
3.2 maximumPoolSize 线程池最大线程数量
一个任务被提交到线程池之后,首先会到工作队列中,如果工作队列满了,则会创建一个新的线程,然后从工作队列中取出一个任务交给新线程处理,而将刚提交上来的任务放入到工作队列中。线程池最大的线程数量由maximunPoolSize来指定。
3.3 keepAliveTime 空闲线程存活时间
一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定的时间后,这个空闲的线程将被销毁,这个指定的时间就是keepAliveTime。
3.4 unit 空闲线程存活时间单位
keepAliveTime的计量单位,是一个枚举java.util.concurrent.TimeUnit。
3.5 workQueue 工作队列
新任务被提交之后,会先进入到此工作队列中,任务调度时再从队列中取出任务。jdk一共提供了四种工作队列。
-
ArrayBlockingQueue 数组型阻塞队列:数组结构,初始化时传入大小,有界,FIFO(先进先出),使用一个重入锁,默认使用非公平锁,入队和出队共用一个锁,互斥。 -
LinkedBlockingQueue 链表型阻塞队列:链表结构,默认初始化大小为Integer.MAX_VALUE,有界(近似无解),FIFO,使用两个重入锁分别控制元素的入队和出队,用Condition进行线程间的唤醒和等待。 -
SynchronousQueue 同步队列:容量为0,添加任务必须等待取出任务,这个队列相当于通道,不存储元素。 -
PriorityBlockingQueue 优先阻塞队列:无界,默认采用元素自然顺序升序排列。 -
DelayQueue 延时队列:无界,元素有过期时间,过期的元素才能被取出。
3.6 threadFactory 线程工厂
创建新线程的时候使用的工厂,可以用来指定线程名,是否为daemon线程等等。
3.7 handler 拒绝策略
当工作队列中的任务已经达到了最大的限制,并且线程池中线程数量达到了最大限制,如果这时候有新任务进来,就会采取拒绝策略,jdk中提供了四种拒绝策略。
-
AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 -
DiscardPolicy:丢弃任务,但是不抛出异常。可能导致无法发现系统的异常状态。 -
DiscardOldestPolicy:丢弃队列最前面的任务,然后重新提交被拒绝的任务。 -
CallerRunsPolicy:由调用线程处理该任务。
四、Executors创建线程池
java.util.concurrent.Executors提供了静态方法创建线程池。
-
newCachedThreadPool :创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,否则新建线程。(线程最大并发数不可控制) -
newFixedThreadPool:创建一个固定大小的线程池,可控制线程最大并发数,超出的线程会在队列中等待。 -
newScheduledThreadPool :创建一个定时线程池,支持定时及周期性任务执行。 -
newSingleThreadExecutor :创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
弊端:
-
newFixedThreadPool和newSingleThreadExecutor:主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。 -
newCachedThreadPool和newScheduledThreadPool:主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
五、总结
我们了解了什么是线程池,线程池有什么用,怎样创建线程池,线程池创建线程执行任务的逻辑。