目錄
- 啟動執行緒的正確和錯誤方式
- 前文回顧
- start 方法和 run 方法的比較
- start 方法分析
- start 方法的含義以及注意事項
- start 方法原始碼分析
- 原始碼
- 原始碼中的流程
- run 方法分析
- run 方法原始碼分析
- 對于 run 方法的兩種情況
啟動執行緒的正確和錯誤方式
前文回顧
- 詳細分析 Java 中實作多執行緒的方法有幾種?(從本質上出發)
start 方法和 run 方法的比較
代碼演示:
/**
* <p>
* start() 和 run() 的比較
* </p>
*
* @author 踏雪彡尋梅
* @version 1.0
* @date 2020/9/20 - 16:15
* @since JDK1.8
*/
public class StartAndRunMethod {
public static void main(String[] args) {
// run 方法演示
// 輸出: name: main
// 說明由主執行緒去執行的, 不符合新建一個執行緒的本意
Runnable runnable = () -> {
System.out.println("name: " + Thread.currentThread().getName());
};
runnable.run();
// start 方法演示
// 輸出: name: Thread-0
// 說明新建了一個執行緒, 符合本意
new Thread(runnable).start();
}
}
從以上示例可以分析出以下兩點:
-
直接使用
run方法不會啟動一個新執行緒,(錯誤方式) -
start方法會啟動一個新執行緒,(正確方式)
start 方法分析
start 方法的含義以及注意事項
-
start方法可以啟動一個新執行緒,-
執行緒物件在初始化之后呼叫了
start方法之后, 當前執行緒(通常是主執行緒)會請求 JVM 虛擬機如果有空閑的話來啟動一下這邊的這個新執行緒, -
也就是說, 啟動一個新執行緒的本質就是請求 JVM 來運行這個執行緒,
-
至于這個執行緒何時能夠運行,并不是簡單的由我們能夠決定的,而是由執行緒調度器去決定的,
-
如果它很忙,即使我們運行了
start方法,也不一定能夠立刻的啟動執行緒, -
所以說
srtart方法呼叫之后,并不意味這個方法已經開始運行了,它可能稍后才會運行,也很有可能很長時間都不會運行,比如說遇到了饑餓的情況, -
這也就印證了有些情況下,執行緒 1 先掉用了
start方法,而執行緒 2 后呼叫了start方法,卻發現執行緒 2 先執行執行緒 1 后執行的情況, -
總結: 呼叫
start方法的順序并不能決定真正執行緒執行的順序, -
注意事項
-
start方法會牽扯到兩個執行緒, -
第一個就是主執行緒,因為我們必須要有一個主執行緒或者是其他的執行緒(哪怕不是主執行緒)來執行這個
start方法,第二個才是新的執行緒, -
很多情況下會忽略掉為我們創建執行緒的這個主執行緒,不要誤以為呼叫了
start就已經是子執行緒去執行了,這個陳述句其實是主執行緒或者說是父執行緒來執行的,被執行之后才去創建新執行緒,
-
-
-
start方法創建新執行緒的準備作業-
首先,它會讓自己處于就緒狀態,
- 就緒狀態指已經獲取到除了 CPU 以外的其他資源, 如已經設定了背景關系、堆疊、執行緒狀態以及 PC(PC 是一個暫存器,PC 指向程式運行的位置) 等,
-
做完這些準備作業之后,就萬事俱備只欠東風了,東風就是 CPU 資源,
-
做完準備作業之后,執行緒才能被 JVM 或作業系統進一步去調度到執行狀態等待獲取 CPU 資源,然后才會真正地進入到運行狀態執行
run方法中的代碼,
-
-
需要注意: 不能重復的執行 start 方法
-
代碼示例
/** * <p> * 演示不能重復的執行 start 方法(兩次及以上), 否則會報錯 * </p> * * @author 踏雪彡尋梅 * @version 1.0 * @date 2020/9/20 - 16:47 * @since JDK1.8 */ public class CantStartTwice { public static void main(String[] args) { Runnable runnable = () -> { System.out.println("name: " + Thread.currentThread().getName()); }; Thread thread = new Thread(runnable); // 輸出: name: Thread-0 thread.start(); // 輸出: 拋出 java.lang.IllegalThreadStateException // 即非法執行緒狀態例外(執行緒狀態不符合規定) thread.start(); } } -
報錯的原因
start一旦開始執行,執行緒狀態就從最開始的 New 狀態進入到后續的狀態,比如說 Runnable,然后一旦執行緒執行完畢,執行緒就會變成終止狀態,而終止狀態永遠不可能再回傳回去,所以會拋出以上例外,也就是說不能回到初始狀態了,這里描述的還不夠清晰,讓我們來看看原始碼能了解的更透徹,
-
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 方法
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 */
}
}
}
原始碼中的流程
第一步:
啟動新執行緒時會首先檢查執行緒狀態是否為初始狀態, 這也是以上拋出例外的原因,即以下代碼:
if (threadStatus != 0)
throw new IllegalThreadStateException();
其中 threadStatus 這個變數的注釋如下,也就是說 Java 的執行緒狀態最初始(還沒有啟動)的時候表示為 0:
/* Java thread status for tools,
* initialized to indicate thread 'not yet started'
*/
private volatile int threadStatus = 0;
第二步:
將其加入執行緒組,即以下代碼:
group.add(this);
第三步:
最后呼叫 start0() 這個 native 方法(native 代表它的代碼不是由 Java 實作的,而是由 C/C++ 實作的,具體實作可以在 JDK 里面看到,了解即可), 即以下代碼:
boolean started = false;
try {
// 第三步, 呼叫 start0 方法
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 方法分析
run 方法原始碼分析
@Override
public void run() {
// 傳入了 target 物件(即 Runnable 介面的實作), 執行傳入的 target 物件的 run 方法
if (target != null) {
target.run();
}
}
對于 run 方法的兩種情況
-
第一種: 重寫了
Thread類的run方法,Thread的run方法會失效, 將會執行重寫的run方法, -
第二種: 傳入了
target物件(即Runnable介面的實作),執行Thread的原有run方法然后接著執行target物件的run方法, -
總結:
-
run方法就是一個普通的方法, 上文中直接去執行run方法也就是相當于我們執行自己寫的普通方法一樣,所以它的執行執行緒就是我們的主執行緒, -
所以要想真正的啟動執行緒,不能直接呼叫
run方法,而是要呼叫start方法,其中可以間接的呼叫run方法,
-
如有寫的不足的,請見諒,請大家多多指教,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/136241.html
標籤:Java
上一篇:java抽象類和抽象方法
