public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
创建新的线程需要获取全局锁,通过这种设计可以避免获取全局锁,当ThreadPoolExecutor
完成预热之后(当前运行的线程数大于等于corePoolSize
),提交的大部分任务都会被放到BlockingQueue
为了形象描述线程池的执行有,打个比喻:
ThreadPoolExecutor
的通用构造函数:
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
corePoolSize
:当有新任务时,如果线程池中线程数没用达到线程池的基本大小,则会创建新的线程执行任务,否则将任务放入阻塞队列。当线程池存活的线程数总是大于corePoolSize
时,应该考虑调大corePoolSize
maxinumPoolSize
:当阻塞队列填满时,如果线程池中线程数没用超过最大线程数,则会创建新的线程运行任务,否则更具拒绝策略处理新任务。非核心线程类似于临时借来的资源,这些线程在空闲时间超过keepAliveTime
之后,就应该退出,避免资源浪费
BlockingQueue
:存储等待运行的任务
keepAliveTime
:非核心线程空闲后,报错存活的时间,此参数只对非核心线程有效。设置为0
,表示多余的空闲线程会呗立刻终止
TimeUnit
:时间单位
TimeUnit.DAYS
TimeUnit.HOURS
TimeUnit.MINUTES
TimeUnit.SECONDS
TimeUnit.MILLISECONDS
TimeUnit.MICROSECONDS
TimeUnit.NANOSECONDS
ThreadFactory
:每当线程池创建一个新的线程时,都是通过线程工厂方法来完成的,在ThreadFactory
中指定要了一个方法newThread
,每当线程池需要创建新线程就会调用它
public class MyThreadFactory implements ThreadFactory {
private final String poolName;
public MyThreadFactory(String poolName) {
this.poolName = poolName;
}
public Thread newThread(Runnable runnable) {
return new MyAppThread(runnable, poolName);//将线程池名字传递给构造函数,用于区分不同线程池的线程
}
}
RejectedExecutionHandler
:当队列和线程池都满了时,根据拒绝策略处理新任务
AbortPolicy:默认的策略,直接抛出RejectedExecutionException
DiscardPolicy:不处理,直接丢弃
DiscardOldestPolicy:将等待队列队首的任务丢弃,并执行当前任务
CallerRunsPolicy:由调用线程处理该任务
ps:注意,这一段是根据源码分析的,具体的没贴出,这只是我的理解
worker
对象,实际传进去的是runnable
任务对象,外层用worker
对象中构造的thread.start
调用其中“run
方法”,run
方法不是runnable
的run
方法,而是worker
对象的run
方法,worker
本身就实现了runnable
接口),其主要代码块会在一个while
函数内,while
函数会判断传进来的runnable
对象(worker
对象传进去的)是否为空,第一次只要传进来的runnable
不为空就直接调用runnable
的run
方法,第二次会去查找阻塞队列中是否含有任务(这是调用getTask
方法),直到从阻塞队列中找到任务后返回runnable
对象为止,说明该线程在创建后一直处于阻塞状态,直到阻塞队列中有任务之后才会进行,如果没有任务继续阻塞,这就解释了为什么线程池的不断重复利用线程的好处和效率问题take()
方法,后者执行的是poll()
方法,其实线程池的核心线程和非核心线程只是我们虚构出来的数据,我们在判断出若当前线程数大于核心线程数时和keepAliveTime
的时长达到后,要除去相应线程是随机的,我们并没有给每个线程标号为核心线程poll
方式取任务的特点是从缓存队列中取任务,最长等待keepAliveTim
e的时长,取不到返回nulltake
方式取任务的特点是从缓存队列中取任务,若队列为空,则进入阻塞状态,直到能取出对象为止总而言之:
getTask()
方法时候销毁,里面有一部分代码块会进行判断),直到线程池中的线程数等于核心线程数,此时这些线程就不会被销毁了(除非你还设置了“核心线程数也可以被销毁”,allowCoreThreadTimeOut
设置为true
,默认是false
),如果减去一个线程就是getTask
方法返回了null
,结束了前面提到的while
循环,并且线程总记录数减一,否则它们就一直处于阻塞状态,等待新的任务到来,核心线程数就保持到我们设定的量了OOM
CPU
资源,这样会导致大量的上下文切换(cpu
给线程分配时间片,当线程的cpu
时间片用完后保存状态,以便下次继续运行),从而增加线程的执行实际,影响了整体执行效率