面试官:Tomcat 在 SpringBoot 中是如何启动的
Posted Think_Higher
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了面试官:Tomcat 在 SpringBoot 中是如何启动的相关的知识,希望对你有一定的参考价值。
文章目录
我们知道SpringBoot给我们带来了一个全新的开发体验,我们可以直接把web程序达成jar包,直接启动,这就得益于SpringBoot内置了容器,可以直接启动,本文将以Tomcat为例,来看看SpringBoot是如何启动Tomcat的,同时也将展开学习下Tomcat的源码,了解Tomcat的设计。
从 Main 方法说起
用过SpringBoot的人都知道,首先要写一个main方法来启动
@SpringBootApplication
public class TomcatdebugApplication
public static void main(
SpringBoot——嵌入式tomcat
面试官:知道线程池吗
我:知道啊,然后准备回答,线程池原理及实现流程
面试官打断:最近面试人挺多的,基本都知道原理,你给我讲讲keepAliveTime具体是怎样实现非核心线程过期回收的。
我:.....(黑人问号)
面试完,迫不及待的打开源码后,发现之前看源码已经看过线程过期后怎样销毁的,但主要是想弄懂线程复用的原理,所以没有注意。
前文源码学习:Java并发包中的线程池ThreadPoolExecutor
一、线程过期销毁
1、Worker.run():线程池线程复用的原理
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); //将state设置为0,允许中断 allow interrupts boolean completedAbruptly = true; try { //这里就是线程复用的实质,线程执行完提交的任务后,会不停从阻塞队列workerQueue中获取任务再执行
//跳出这里的循环,线程就不会复用,执行完runWorker方法后会被销毁 while (task != null || (task = getTask()) != null) { w.lock();//worker不可重入独占锁 // 如果线程池正在停止Stopping,确保线程中断 // 如果没有,确保线程没有被中断 // 第二种情况需要复查处理 // 清除中断时立即停止比赛 if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { //执行任务前操作--空方法预留扩展 beforeExecute(wt, task); Throwable thrown = null; try { //实际的任务运行command.run() task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { //执行任务后操作--空方法预留扩展 afterExecute(task, thrown); } } finally { task = null; //当前线程完成任务数更新++ w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { //执行清理工作, //将当前线程完成任务数累加到总任务数上 //从workerSet中删除当前工作线程 //尝试终止线程池 //如果当前线程个数小于核心个数,创建线程 processWorkerExit(w, completedAbruptly); } }
2、线程过期销毁
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); //开始执行前会将work.firstTask = null,核心线程、非核心线程都是 Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { //一次循环后task == null,所以线程过期销毁判断关键在于getTask() while (task != null || (task = getTask()) != null) { w.lock(); if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { beforeExecute(wt, task); Throwable thrown = null; try { task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { afterExecute(task, thrown); } } finally { //一次循环后task = null task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { processWorkerExit(w, completedAbruptly); } }
ThreadPoolExecutor.getTask()
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//如果工作队列为空,线程池线程数减1,并返回null decrementWorkerCount(); return null; } int wc = workerCountOf(c); //worker工作线程会不会被回收
//① allowCoreThreadTimeOut == true,核心线程和非核心线程空闲keepAliveTime被销毁,workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
//② allowCoreThreadTimeOut == false,非核心线程空闲keepAliveTIme销毁,workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
// ,核心线程会被阻塞,不会被销毁workQueue.take() boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
所以keepAliveTime具体是怎样实现非核心线程过期销毁的,是个很简单的问题。有点后悔没答上来
workQueue队列为空,跳出循环,线程就会被销毁。具体代码通过阻塞队列workQueue(keepAliveTime,TimeUnit.NANOSECONDS)实现控制线程存活时间的
线程销毁的两种场景:
① allowCoreThreadTimeOut == true,核心线程和非核心线程空闲keepAliveTime时被销毁,workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
② allowCoreThreadTimeOut == false,非核心线程空闲keepAliveTIme时被销毁,workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
核心线程永不销毁的场景
① allowCoreThreadTimeOut == false,核心线程永不销毁。workQueue.take()阻塞当前线程。
以上是关于面试官:Tomcat 在 SpringBoot 中是如何启动的的主要内容,如果未能解决你的问题,请参考以下文章
面试官:如何手写一个Spring Boot Starter?