Hello,騷年們,你們新年快樂,頭髮有沒有少呀?今天咱們來看一件有趣的事,首先來看段代碼java
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
service.submit(() -> System.out.println("Hello "));
System.out.println("World");
}
複製代碼
呵呵,執行結果誰都知道,顯而易見jvm
可是小老弟,有沒有發現這個程序 一直都沒有結束呢?明明這個任務都已經跑完了呀~開始了嗎?很差意思已經結束了,嘻嘻,大過年的不賣關子,咱們直接公佈答案,形成不退出的緣由是這樣:源碼分析
submit
操做會建立Worker
線程(負責去拿任務處理),該線程裏寫了一個死循環,因此這個Worker
線程不會死Worker
線程在建立的時候,被設置成了非守護線程,thread.setDaemon(false)
JDK1.5
的時候,就規定了當全部非守護線程退出時,JVM
纔會退出,Main
方法主線程和Worker
線程都是非守護線程,因此不會死。下面咱們會就上面幾個問題,每個問題進行源碼分析,感興趣的看官老爺能夠繼續,看看又不會掉髮(逃this
夢開始的地方先從初始化開始spa
//該方法利用多臺實例化了一個ThreadPoolExecutor線程池,該線程池繼承了一個抽象類AbstractExecutorService
ExecutorService service = Executors.newFixedThreadPool(10);
//調用了ThreadPoolExecutor.submit方法也就是父類的AbstractExecutorService.submit,該方法內部會去調用execute()方法
service.submit(() -> System.out.println("Hello "));
複製代碼
因而咱們定位到ThreadPoolExecutor
類的execute
方法,我截取了部分以下,注意代碼中我打註釋的地方線程
public void execute(Runnable command) {
...
//若是工做線程尚未超過核心線程數
if (workerCountOf(c) < corePoolSize) {
//去添加工做線程
if (addWorker(command, true))
return;
}
...
複製代碼
線程池把每個運行任務的工做線程抽象成了Worker
,咱們定位到內部addWorker
方法3d
...
//新建一個Worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//下面的操做是將線程添加到工做線程集合裏
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//若是添加成功的話
if (workerAdded) {
//把工做線程跑起來
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
複製代碼
這時候一個工做線程也就跑起來了,能夠去執行任務了,咱們定位到ThreadPoolExecutor
的內部類Worker
的run
方法裏code
//該類調用了runWorker方法
public void run() {
runWorker(this);
}
複製代碼
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//主要看這個while,會看這個Worker有沒有任務,若是沒有就會去取,這裏是一個死循環,而後咱們定位到getTask()方法,看他是怎麼取任務的
while (task != null || (task = getTask()) != null) {
w.lock();
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
...
}
複製代碼
這裏解釋了,工做線程其實不會死(超時時間不在本期範圍內),咱們繼續定位到內部的getTask()
方法,看他是怎麼取任務的cdn
private Runnable getTask() {
...
//有沒有設置核心線程超時時間(默認沒有)當前工做的線程數大於了線程池的核心線城市
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
...
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
//調用workQueue的Take方法,WorkQueue默認是一個BlockingQueue,因此調用take方法會致使當前工做線程阻塞掉,指到拿到
workQueue.take();
//若是拿到任務就返回
if (r != null)
return r;
timedOut = true;
...
複製代碼
這裏想說的有兩點:blog
BlockingQueue
的take()
拿不到任務會阻塞首先咱們來到ThreadPoolExecutor
的構造方法裏
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
複製代碼
構造器裏傳入了一個ThreadFactory
也就是Executors.defaultThreadFactory()
,用來產生工做線程,一步一步的點進去咱們會定位到Executors
內部類DefaultThreadFactory
的newThread
方法
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
//關鍵代碼是這裏,把線程設置成了非守護線程
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
複製代碼
而後咱們看ThreadPoolExector
方法去new Worker()的時候
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
//這裏的ThreadPool,就是上面提到的那個生產非守護線程的線程工廠
this.thread = getThreadFactory().newThread(this);
}
複製代碼
看上面的註釋下面的內容,爲何是非守護線程就真相大白了。
JdkDoc
注意我標藍的部分,這跟jvm的實現有關,先知道結論,具體爲何咱們留着下期再講~
跟同事在codeReview
的時候,忽然聊到單啓動線程池,Main方法會不會死明明已經都結束了呀,而後就本地跑了試了一下,跟平常的理解仍是不同的,查了一下緣由,仍是蠻有趣的,平常工做中多保持好奇心,不要怕難,你會愈來愈強的!
你變強了,也變禿了(逃