大家好,我是奇喵小屋,一个分享技术和生活的博主。以下是我的主页。关注掘金主页发布更多MySQL、Redis、并发、JVM、分布式等面试热点知识,以及Java学习路线、面试重点、职业规划、面经等相关博文转载请注明来源!1.Executor框架介绍1.1任务的两级调度模型应用程序通过Executor框架控制上层调度。底层调度由操作系统内核控制,不受应用程序控制。1.2Executor架构结构Executor架构分为三部分。执行任务2.Future接口用于控制任务的执行,获取异步任务的执行状态,将Callable任务作为执行结果提交给线程池,线程池会返回一个Future对象用于我们来查看异步任务的执行状态。执行结果为Futurefuture=executor.submit(newCallable(){});//上下等效RunnableFuturefuture=newFutureTask(asynchronoustask);executor.execute(未来);3.ThreadPoolExecutor详解3.1线程池的状态线程池有五种运行状态RUNNING(运行中)——可以接收新任务并执行SHUTDOWN(关闭)——不再接收新任务,但仍会处理已经处理过的任务submitted(包括正在运行的线程和阻塞队列中的线程)STOP(停止)——不会接收新的任务,阻塞队列中的任务不会被处理,正在执行的任务会被打断TIDYING(排序)——所有任务都已完成终止,将线程池状态转换为TIDYING的线程会调用terminated()TERMINATED(终止)——terminated()已执行完毕,线程池终止。线程池维护一个AtomicInteger变量来表示线程池的状态(这个变量也可以表示线程池中的线程数)线程池状态变化3.2手动创建线程池publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueueworkQueue,ThreadFactorythreadFactory,RejectedExecutionHandlerhandler){//略this.corePoolSize=corePoolSize;this.maximumPoolSize=maximumPoolSize;(保持活动时间);this.threadFactory=threadFactory;this.handler=处理程序;}参数介绍corePoolSize:初始化ThreadPoolExecutor中的corePoolSize,指定核心线程数maximumPoolSize:初始化ThreadPoolExecutor中的maximumPoolSize,代表线程池中允许的最大线程数keepAliveTime:初始化ThreadPoolExecutor中的keepAliveTime,允许空闲线程存活的最长时间unit:keepAliveTime的单位workQueue:在ThreadPoolExecutor中初始化workQueue,任务队列threadFactory:在ThreadPoolExecutor中初始化threadFactory,线程工厂,用于创建线程handler:在handler中初始化threadPoolExecutor,拒绝策略ThreadPoolExecuTor内置了4种拒绝策略AbortPolicy:直接抛出异常CallerRunsPolicy:由提交任务的线程处理任务DiscardOldestPolicy:丢弃队列中最早的任务,重新提交被拒绝的任务DiscardPolicy:不处理,丢弃。3.3execute()执行过程%E8%BF%87%E7%A8%8B.png)process](../p/execute()process.png)3.4Woker和addWork()逻辑线程池,线程封装为Workerthread:firstTask由线程池中的threadFactory创建:创建Worker时,可以指定firstTask,如果firstTask不为null,则线程会先执行firstTask,创建核心线程失败。线程池状态为STOP、TIDYING、TERMINATED线程池状态为SHUTDOWN,传入任务不为null(处于SHUTDOWN状态,线程池不再接受新任务)当前线程数>=数量ofcorethreadsallowedcreatenon-corethreadsfailed,thethreadpoolstatusisSTOP,TIDYING,ThestatusofTERMINATEDthreadpoolisSHUTDOWN,传入的任务不为空(在SHUTDOWN状态下,线程池没有longer接受新任务)当前线程数>=最大允许线程数3.5Worker工作进程Woker执行2个任务Source创建Woker时指定的firstTask从阻塞队列中获取Woker,并将其分为阻塞核心线程和非阻塞线程corethreads——根据当前线程数是否<=corePoolSize来判断(所以对于同一个线程,可以某个时刻是核心线程,另一个时刻可以是非核心线程)默认情况下,核心线程会永久阻塞阻塞队列中的获取,不会被销毁。非核心线程只会阻塞在阻塞队列中获取keepAliveTime时间,超过就会销毁。但是如果线程池设置了allowCoreThreadTimeOut,那么核心线程的处理和非核心线程一样3.6关闭线程池3.6.0尝试销毁线程池——tryTerminate()尝试改变线程池的状态线程池为TERMINATED,只有以下两种情况才能成功,线程池状态为SHUTDOWN,线程池中没有线程,阻塞队列为空。线程池STOP,线程池中没有线程。改变线程池的状态为SHUTDOWN在线程池中的所有线程上调用其interrupt()传递中断信号调用tryTerminal()尝试销毁线程池第4步在大多数情况下不会成功线程的状态池更改为SHUTDOWN之后,线程池将不再接受新的任务,但是已经接受的任务会继续执行。当所有任务都执行完后,线程检测到线程池状态为SHUTDOWN,任务队列为空,则线程会执行退出操作——在退出操作中,每个线程都会执行一次tryTerminal(),最后执行一次tryTerminal()退出的线程可以成功销毁线程池改变线程池的状态为STOP对线程池中的所有线程调用interrupt()传递中断信号从任务队列中移除所有未执行的任务调用tryTerminal()尝试销毁threadpoolshutdownNow()从任务队列中移除所有未执行的任务3.7预热方式3.7.1prestartCoreThread在线程池中预创建一个线程3.7.2prestartAllCoreThreads在线程池中创建所有核心线程3.8面试题3.8.1如何理解keepAliveTime如果线程池中的线程数>corePoolSize,那么一旦冗余线程空闲时间超过keepAliveTime,线程就会被销毁,直到线程数==corePoolSize3.8.2任务为什么要放到任务队列中first而不是直接把线程数拉到最大个人认为线程池的初衷是让核心线程数工作,任务队列起到缓冲区的作用。最大线程数这个参数更像是无奈之举。当有很多任务时,这是最后的努力。创建新线程以帮助处理任务。本机线程池往往是CPU密集型的。当任务太多时,它不会创建更多的线程,而是先缓存任务,让核心线程处理它们。像Tomcat这样的业务场景是IO密集型的,原生线程池不合适,需要定制(Tomcat的线程池是定制的)4.ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,用于在给定的延迟后执行任务或者执行定时任务。默认任务队列是使用DelayWorkQueue提交任务,任务封装为ScheduledFutureTask后直接进入任务队列,然后线程从任务队列中获取ScheduledFutureTask执行。duleAtFixedRate()、scheduleWithFixedDelay()、submit()、execute()的逻辑基本相同。下面以schedule()为例进行说明.png)在ScheduledThreadPool中,任务被封装为ScheduledFutureTask后,直接进入任务队列,然后线程开始从任务队列中获取ScheduledFutureTask并执行4.3执行结构DelayWorkQueue和ScheduledFutureTask的Task执行步骤Thread从DelayWorkQueue中获取超时的ScheduledFutureTask(从队列中获取queue[0],如果没有则阻塞等待,然后检查任务是否超时,如果任务没有超时,会一直阻塞直到任务超时)执行任务线程的线程修改ScheduledFutureTask的时间为下次执行的时间。线程将ScheduledFutureTask放回DelayWorkQueue5。FutureTaskFutureTask可以直接由调用线程(FutureTask.run())执行(该方法不会创建新线程),也可以提交到线程池中执行。FutureTask和Future一样,可以控制任务的执行状态,获取任务的执行结果。5.1FutureTask状态转换图