為什麼啟動線程是start方法 十年可見春去秋來,百年可證生老病死,千年可嘆王朝更替,萬年可見鬥轉星移。 凡人如果用一天的視野,去窺探百萬年的天地,是否就如同井底之蛙? 背景:啟動線程是start() 還是run() 方法?相信這個問題很多人都知道是start(),但是如果我再問下去呢,為什麼是st ...
為什麼啟動線程是start方法
十年可見春去秋來,百年可證生老病死,千年可嘆王朝更替,萬年可見鬥轉星移。
凡人如果用一天的視野,去窺探百萬年的天地,是否就如同井底之蛙?
背景:啟動線程是start() 還是run() 方法?相信這個問題很多人都知道是start(),但是如果我再問下去呢,為什麼是start()?你會如何作答呢?
一、理論課
當用start()開始一個線程後,線程就進入就緒狀態,使線程所代表的虛擬處理機處於可運行狀態,這意味著它可以由JVM調度並執行;但是這並不意味著線程就會立即運行,只有當cpu分配時間片時,這個線程獲得時間片時,才開始執行run()方法;start()方法去調用run(),而run()方法則是需要去重寫的,其包含的是線程的主體(真正的邏輯)。
二、線程六大狀態
Java 中,定義了 6 種線程狀態,NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED。
1 public enum State{
2 NEW,
3 RUNNABLE,
4 BLOCKED,
5 WAITING,
6 TIMED_WAITING,
7 TERMINATED;
8 }
6 種線程狀態關係圖
三、代碼層次
楊總說的,一切問題歸咎於源碼!
start 方法的源碼:
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
// 未初始化則拋異常
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
// 是否啟動標誌符
boolean started = false;
try {
/**
* start0() 是啟動多線程的關鍵
* 執行完成之後,新的線程已經在運行了
* 這裡會創建一個新的線程,是一個 native 方法
*/
start0();
// 主線程執行
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
run方法源碼:
@Override
public void run() {
// 簡單的運行,不會新起線程,target 是 Runnable
if (target != null) {
target.run();
}
}
從start()和run()方法的源碼可以看出,start方法的源碼也沒幾行代碼,最主要的是 start0() 方法;run() 方法的源碼也比較簡單的,就是一個普通方法的調用;重點是這個 start0() 方法,她是真正實現多線程的關鍵。
start0方法源碼:
// native :就是本地方法
private native void start0();
關於native方法簡述:
1、被native關鍵字修飾的方法叫做本地方法,本地方法和其它方法不一樣,本地方法意味著和平臺有關,因此使用了native的程式可移植性都不太高;
2、native方法在JVM中運行時數據區也和其它方法不一樣,它有專門的本地方法棧;
3、native方法主要用於載入文件和動態鏈接庫,由於Java語言無法訪問操作系統底層信息(比如:底層硬體設備等),這時候就需要藉助C語言來完成了;
4、被native修飾的方法可以被C語言重寫。
Java 跨平臺圖
在start()中start0 被標記成 native ,也就是本地方法,並不需要我們去實現或者瞭解,只要瞭解下為什麼 start0() 會標記成 native ?
如上圖,start() 方法調用 start0() 方法後,該線程並不一定會立馬執行,只是將線程變成了可運行狀態(NEW ---> RUNNABLE);具體什麼時候執行,取決於 CPU ,由 CPU 統一調度;我們又知道 Java 是跨平臺的,可以在不同系統上運行,每個系統的 CPU 調度演算法不一樣,所以就需要做不同的處理,這件事情就只能交給 JVM 來實現了,start0() 方法自然就表標記成了 native。
四、總結
Java 中實現真正的多線程是 start 中的 start0() 方法,run() 方法只是一個包含業務邏輯普通的方法;start是啟動多線程的唯一方式,其使得線程由創建態到就緒態,而這個線程是否被運行是由系統調度所決定的。
十年可見春去秋來
百年可證生老病死
千年可嘆王朝更替
萬年可見鬥轉星移
凡人如果用一天的視野
去窺探百萬年的天地
是否就如同井底之蛙?