文章目录
线程池的优势
解决资源开销避免重复的new Thread对象,重复使用一个线程对象。
用户线程(UT),内核线程(KLT)
用户线程: 应用层自己管理线程
内核线程:操作系统管理线程
JVM hostport使用的是什么线程
内核线程(KLT)
ThreadPoolExector线程池参数
- 核心线程数(corePoolsize)
- 最大线程数(MaxmumpoolSize)
- 线程生存时间(keepAliveTime)
- 生存时间单位(unit)
- 工作队列(wordQueue)
阻塞队列 > (假设队列满了,所有入列操作必须等待,也就是被堵塞。 如果队列为空,只能进行入列操作,所有出列操作必须等待,也就是被堵塞)
- 线程工程(ThreadFactory)
- 拒绝策略(handler)
如何最大线程数和队列全部占满如何处理的方式
- AbortPolicy(默认策略)
这是默认的拒绝策略。当线程池饱和时,新任务的提交将会抛出RejectedExecutionException,通知调用者发生了拒绝。- CallerRunsPolicy(调用者线程中执行)
当线程池饱和时,新任务会在调用者线程中直接执行。这种策略可能会导致调用者线程被阻塞,因为它本来可能想要提交新的任务。- DiscardPolicy(直接丢弃任务)
当线程池饱和时,新任务将被丢弃,没有任何处理。这可能导致丢失任务,不推荐在需要完全处理所有任务的情况下使用- DiscardOldestPolicy (丢弃最早的任务)
当线程池饱和时,尝试将最早加入队列的任务丢弃,以便为新任务腾出空间。
线程池存放过程和执行过程
- 提交存放过程
顺序 核心线程 —> 阻塞队列 —> 最大线程
- 流程解释
当任务提交之后,线程池首先会检查当前线程数,如果当前的线程数小于核心线程数(corePoolSize),比如最开始创建的时候线程数为 0,则新建线程并执行任务。
当提交的任务不断增加,创建的线程数等于核心线程数(corePoolSize),新增的任务会被添加到 workQueue 任务队列中,等待核心线程执行完当前任务后,重新从 workQueue 中获取任务执行。
假设任务非常多,达到了 workQueue 的最大容量,但是当前线程数小于最大线程数(maximumPoolSize),线程池会在核心线程数(corePoolSize)的基础上继续创建线程来执行任务。
假设任务继续增加,线程池的线程数达到最大线程数(maximumPoolSize),如果任务继续增加,这个时候线程池就会采用拒绝策略来拒绝这些任务。
线程池生命周期
Running
能接受新任务以及处理已添加的任务
Shutdown
不接受新任务,可以处理已经添加的任务
Stop
不接受新任务,不处理已经添加的任务,并且中断正在处理的任务
Tidying
所有的任务已经终止,记录的”任务数量”为0,负责记录线程池的运行状态与活动线程数量
Terminated
线程池彻底终止,则线程池转变为terminated状态
线程池是如何实现线程的复用
代码
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 释放锁 设置work的state=0 允许中断
boolean completedAbruptly = true;
try {
//一直执行 如果task不为空 或者 从队列中获取的task不为空
while (task != null || (task = getTask()) != null) {
task.run();//执行task中的run方法
}
completedAbruptly = false;
} finally {
//1.将 worker 从数组 workers 里删除掉
//2.根据布尔值 allowCoreThreadTimeOut 来决定是否补充新的 Worker 进数组 workers
processWorkerExit(w, completedAbruptly);
}
}
解释:
可以看到,实现线程复用的逻辑主要在一个不停循环的 while 循环体中。
通过获取 Worker 的 firstTask 或者通过 getTask 方法从 workQueue 中获取待执行的任务
直接通过 task.run() 来执行具体的任务(而不是新建线程),一个线程在一个while中不停的执行其他run方法从而实现线程复用。