线程池中的线程是怎么创建的?是一开始就随着线程池的启动创建好的吗?

线程池中的线程甚至在启动时都没有创建。
它们与线程池处理任务的实时性需求密切相关。
每当有任务提交到线程池时,线程池就会启动一系列进程来动态创建线程。
这个过程的本质是,线程池启动时,并不是一次性创建所有线程,而是根据预设的核心线程数提前启动。
如果在启动线程池时将prestartAllCoreThreads参数设置为true,那么线程池会预先创建一些等于核心线程数的线程并启动它们。
此步骤的目的是提前准备资源,避免提交作业时因资源不足而造成延误。
但是,提前启动的线程数量并不意味着线程池在启动时就已经完成了所有线程的创建。
随着任务的不断提交,线程池会根据实际需要动态创建和销毁线程。
这种动态管理机制使得线程池能够有效平衡任务处理和资源消耗,保证资源合理分配的同时避免资源浪费。
通过动态创建线程,线程库可以灵活应对突发的任务提交,保证任务高峰时的快速响应,提高系统的整体性能和响应速度。
同时,这种机制也让线程池能够更有效地控制资源使用情况,避免资源过度分配,从而提高系统的稳定性和可靠性。

创建线程的方式

1、继承Thread类,创建Thread子类1、在该子类中重写run方法,在run方法中写入线程任务代码2、创建子类的实例,即创建Thread实例3、调用实例start方法启动线程2、创建一个类,实现Runnable接口1、该类实现该接口的run方法,在run方法中写入线程任务代码2。
创建此类的实例,并将该实例作为Thread类的标记目标传递,如下所示:Thread=newThread(此类的实例);即创建一个线程对象3。
调用线程的star方法来启用该线程。

线程池ThreadPoolTaskExecutor配置

参数说明:corePoolSize:线程池维护线程的最小数量maxPoolSize:线程池维护线程的最大数量keepAliveSeconds:(maxPoolSize-corePoolSize)某些线程在不活动状态下可以存活的最长时间tailCapacity:阻塞活动队列EnableCoreThreadTimeOut:如果设置为true,keepAliveSeconds参数设置的有效时间对于corePoolSize线程也有效:当提交的任务数量超过maxmumPoolSize+workQueue的总和,该任务将被传递给RejectedExecutionHandler来处理该线程。

创建过程:1.当线程数小于corePoolSize时,创建线程,无论线程是否空闲2.当线程数大于等于corePoolSize时,将任务放入codeCapacity队列中。
3.当Capacity队列满时,创建新的Execute线程4.当线程数大于等于maxPoolSize时,根据RejectedExecutionHandler设置的策略来处理新添加的任务,执行500个任务。
每个任务需要6秒来执行。
启动并运行最多12个线程后,如果再次调用该接口,将会有7个线程(随机)死亡,这次会创建7个新线程(名称与之前死亡的7个线程不同)。
如果在线程消亡之前再次调用该接口,则仍然会使用当前的12个线程。

java线程池工作队列是如何工作的

使用线程池的好处

1减少资源消耗

可以复用已创建的线程,减少创建和销毁带来的消耗。

2提高响应速度

当任务到来时,可以立即执行任务,无需等待线程创建。

3提高线程可管理性

线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统稳定性。
使用线程池,可以进行统一分配、调优和监控

线程池是如何工作的

首先我们看一下线程池是如何处理new出来的任务的他。
提交到线程池

1线程池判断主线程池中的所有线程是否都在执行任务。
如果没有,则创建一个新的工作线程来执行该任务。
如果主线程池中所有线程都在执行任务,则执行第二步。

2线程池判断工作队列是否已满。
如果工作队列未满,则新提交的任务将保存在该工作队列中并等待。
如果工作队列已满,则执行第三步

3线程池判断线程池中的所有线程是否都处于工作状态。
如果没有,则创建一个新的工作线程来执行该任务。
如果满了,就交给饱和策略来处理任务

线程池饱和策略

这里提到了线程池饱和策略,那么我们简单介绍一下什么是饱和is策略:

AbortPolicy

是默认的Java线程池阻塞策略,它不执行这个任务,直接在运行时抛出异常正确,否则程序将直接退出。

DiscardPolicy

直接丢弃,任务未执行,空方法

DiscardOldestPolicy

从队列中移除一个头任务并再次重复运行这个任务。

CallerRunsPolicy

在调用者运行组上执行此命令将阻止登录

用户定义的拒绝策略(最常用)

实现RejectedExecutionHandler并定义你的策略模式

让我知道我们以ThreadPoolExecutor为例展示池工作流程图thread

1.jpg

2.jpg

1如果线程数当前运行的线程小于corePoolSize,创建一个新线程来执行任务(注意必须获得全局锁才能执行这一步)。

2如果正在运行的线程等于或大于corePoolSize,则将任务添加到BlockingQueue中。

3如果任务无法添加到BlockingQueue(队列已满),则在非核心池中创建一个新线程来处理该任务(注意,执行此操作必须获得全局锁)步)。

4如果创建新线程会导致当前正在执行的线程超过最大PoolSize,则该任务将被拒绝,并且将调用RejectedExecutionHandler.rejectedExecution()方法。

采取上述步骤的ThreadPoolExecutor的总体设计思想是在execute()方法的执行过程中尽可能避免获取全局锁(这将是扩展的严重瓶颈)。
ThreadPoolExecutor完成预热后(当前执行线程数大于等于corePoolSize),几乎所有对execute()方法的调用都会执行步骤2,并且步骤2不需要获取全局锁。

线程池只是并发编程的一小部分,下图是史上最全面的Java并发编程学习技巧总结

3.jpg

main方法源码分析

我们看一下添加到线程池方法run的main方法源码如下:

/////未来某个时间执行给定的任务//可以在新批次上执行。
导致容量达到,//thetaskshandledbythecurrent{@codeRejectedExecutionHandler}//@paramcommandthetasktoexecute//@throwsRejectedExecutionExc。
eptionatdiscretionof//{@codeRejectedExecutionHandler},ifthetask//无法接受执行//@throwsNullPointerExceptionif{@codecommand}为null// publicvoidexecute(Runnablecommand){if(command==null)thrownewNullPointerException();分3步进行://1.如果poolSizethreadarrange很大,则尝试//startaneewthreadmethegivencommandasitsfirst//ThecalltoaddWorkeratomicallychecksrunStateand//workerCount,andsopventsfalsealarmsthatdouldd,byreturningfalse//翻译如下输入任务通过addWord方法创建一个新线程,//如果新线程的创建可以完成,则run方法完成并且任务提交成功//2.如果任务可以成功设置,那么我们必须//仔细检查是否应该添加dathread/(现有线程在检查时死亡)或//thepoolshutdownsincentryinthismethod.Sowe//recheckstateandifnecessaryrollbacktheenqueuingif//stop,orstartewthreadevereenone。
另一个检查是否Status//在任务被添加到队列后,它变得空闲(此时线程池可能被关闭,当然//被拒绝)。
当前线程数为0(有可能此时线程数已经改变为0),如果是,则添加一个新线程//3.如果我们无法入队,则wetrytoaddanew//wordif失败,称为done或full//并拒绝Thetask//翻译如下://如果任务无法添加到工作队列中,它将尝试使用该任务来添加新的线程池已达到饱和,因此拒绝下一个任务//intc=ctl.get(//工作线程数小于核心线程数if(workerCountOf(c)ofworkerthreads大于等于core;//线程状态不是RUNNING且队列未满if(isRunning(c)&&workQueue.offer(command)){//再次检查线程的运行状态,如果是不是RUNNINGNG则直接从队列中移除intrecheck=ctl.get(if(!isRunning(recheck)&&remove(command))//移除成功,拒绝未执行的任务;Reject(commandelseif(workerCountOf(recheck)==0)//防止SHUTDOWN状态下没有活动线程,但队列中还有任务没有执行的特殊情况//添加无效任务发生这种情况是因为在SHUTDOWN状态下,线程池不再接受新任务。