1.池化背景
在面向对象编程中,创建和销毁对象是很费时间的,因为创建一个对象要获取内存资源或者其它更多资源。在Java中更是如此,虚拟机将试图跟踪每一个对象,以便能够在对象销毁后进行垃圾回收。所以提高服务程序效率的一个手段就是尽可能减少创建和销毁对象的次数,特别是一些很耗资源的对象创建和销毁。如何利用已有对象来服务就是一个需要解决的关键问题,其实这就是一些"池化资源"技术产生的原因 。
2.java线程池的优势
(1):降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
(2):提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
(3):提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用线程池,必须对其实现原理了如指掌
3.线程池实现原理
当向线程池提交一个任务之后,线程池是如何处理这个任务的呢?本节来看一下线程池的主要处理流程,处理流程图如图3-1所示:

从图中可以看出,当提交一个新任务到线程池时,线程池的处理流程如下。 1)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。 2)线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程。 3)线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。 ThreadPoolExecutor执行execute()方法的示意图,如图3-2所示:

ThreadPoolExecutor执行execute方法分下面4种情况。 1)如果当前运行的线程少于corePoolSize,则创建新线程来执行任务(注意,执行这一步骤需要获取全局锁)。 2)如果运行的线程等于或多于corePoolSize,则将任务加入BlockingQueue。 3)如果无法将任务加入BlockingQueue(队列已满),则创建新的线程来处理任务(注意,执行这一步骤需要获取全局锁)。 4)如果创建新线程将使当前运行的线程超出maximumPoolSize,任务将被拒绝,并调用RejectedExecutionHandler.rejectedExecution()方法。 ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。 源码分析:上面的流程分析让我们很直观地了解了线程池的工作原理,让我们再通过源代码来看看是如何实现的,线程池执行任务的方法如下。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//获取clt,clt记录着线程池状态和运行线程数。
int c = ctl.get();
//运行线程数小于核心线程数时,创建线程放入线程池中,并且运行当前任务。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
//创建线程失败,重新获取clt。
c = ctl.get();
}
//线程池是运行状态并且运行线程大于核心线程数时,把任务放入队列中。
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//重新检查线程池不是运行状态时,
//把任务移除队列,并通过拒绝策略对该任务进行处理。
if (! isRunning(recheck) && remove(command))
reject(command);
//当前运行线程数为0时,创建线程加入线程池中。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//运行线程大于核心线程数时并且队列已满时,
//创建线程放入线程池中,并且运行当前任务。
else if (!addWorker(command, false))
//运行线程大于最大线程数时,失败则拒绝该任务
reject(command);
在execute()方法中多次调用addWorker方法。其源码如下
//获取线程池的运行状态。
int rs = runStateOf(c);
//线程池处于关闭状态,或者当前任务为null
//或者队列不为空,则直接返回失败。
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;

本文详细解析了Java线程池的实现原理,包括池化背景、优势、核心线程、最大线程、任务队列和饱和策略。线程池通过减少对象创建销毁、提高响应速度和便于管理,提升服务效率。执行流程涉及核心线程检查、工作队列处理和线程创建。合理配置线程池需考虑任务性质、优先级、执行时间和依赖性等因素。监控线程池可通过taskCount、completedTaskCount等属性及扩展线程池实现。

658

被折叠的 条评论
为什么被折叠?



