线程组多次调用_详细分析 Java 中启动线程的正确和错误方式
start 方法和 run 方法的比較
代碼演示:
/**
?*
?* start() 和 run() 的比較
?*
?*
?* @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 方法,第二個才是新的線程。
很多情況下會忽略掉為我們創建線程的這個主線程,不要誤以為調用了 start 就已經是子線程去執行了,這個語句其實是主線程或者說是父線程來執行的,被執行之后才去創建新線程。
線程對象在初始化之后調用了 start 方法之后, 當前線程(通常是主線程)會請求 JVM 虛擬機如果有空閑的話來啟動一下這邊的這個新線程。
也就是說, 啟動一個新線程的本質就是請求 JVM 來運行這個線程。
至于這個線程何時能夠運行,并不是簡單的由我們能夠決定的,而是由線程調度器去決定的。
如果它很忙,即使我們運行了 start 方法,也不一定能夠立刻的啟動線程。
所以說 srtart 方法調用之后,并不意味這個方法已經開始運行了。它可能稍后才會運行,也很有可能很長時間都不會運行,比如說遇到了饑餓的情況。
這也就印證了有些情況下,線程 1 先掉用了 start 方法,而線程 2 后調用了 start 方法,卻發現線程 2 先執行線程 1 后執行的情況。
總結: 調用 start 方法的順序并不能決定真正線程執行的順序。
注意事項
start 方法會牽扯到兩個線程。
第一個就是主線程,因為我們必須要有一個主線程或者是其他的線程(哪怕不是主線程)來執行這個 start 方法,第二個才是新的線程。
很多情況下會忽略掉為我們創建線程的這個主線程,不要誤以為調用了 start 就已經是子線程去執行了,這個語句其實是主線程或者說是父線程來執行的,被執行之后才去創建新線程。
需要注意: 不能重復的執行 start 方法
代碼示例
/**
*
* 演示不能重復的執行 start 方法(兩次及以上), 否則會報錯
*
*
* @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'
?*/第二步:
將其加入線程組。即以下代碼:
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 方法源碼分析
@Overridepublic?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 方法。
原文鏈接:cnblogs.com/txxunmei/p/13747631.html
總結
以上是生活随笔為你收集整理的线程组多次调用_详细分析 Java 中启动线程的正确和错误方式的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: redis最大储存512m_redis系
- 下一篇: java判断一个对象是否为空_Java中