日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

线程组多次调用_详细分析 Java 中启动线程的正确和错误方式

發(fā)布時間:2023/11/27 java 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 线程组多次调用_详细分析 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// 說明由主線程去執(zhí)行的, 不符合新建一個線程的本意
????????Runnable runnable = () -> {
????????????System.out.println("name: "?+ Thread.currentThread().getName());
????????};
????????runnable.run();// start 方法演示// 輸出: name: Thread-0// 說明新建了一個線程, 符合本意new?Thread(runnable).start();
????}
}

從以上示例可以分析出以下兩點:

  • 直接使用 run 方法不會啟動一個新線程。(錯誤方式)

  • start 方法會啟動一個新線程。(正確方式)

start 方法分析

start 方法的含義以及注意事項

start 方法可以啟動一個新線程。

第一個就是主線程,因為我們必須要有一個主線程或者是其他的線程(哪怕不是主線程)來執(zhí)行這個 start 方法,第二個才是新的線程。

很多情況下會忽略掉為我們創(chuàng)建線程的這個主線程,不要誤以為調用了 start 就已經是子線程去執(zhí)行了,這個語句其實是主線程或者說是父線程來執(zhí)行的,被執(zhí)行之后才去創(chuàng)建新線程。

線程對象在初始化之后調用了 start 方法之后, 當前線程(通常是主線程)會請求 JVM 虛擬機如果有空閑的話來啟動一下這邊的這個新線程。

也就是說, 啟動一個新線程的本質就是請求 JVM 來運行這個線程。

至于這個線程何時能夠運行,并不是簡單的由我們能夠決定的,而是由線程調度器去決定的。

如果它很忙,即使我們運行了 start 方法,也不一定能夠立刻的啟動線程。

所以說 srtart 方法調用之后,并不意味這個方法已經開始運行了。它可能稍后才會運行,也很有可能很長時間都不會運行,比如說遇到了饑餓的情況。

這也就印證了有些情況下,線程 1 先掉用了 start 方法,而線程 2 后調用了 start 方法,卻發(fā)現線程 2 先執(zhí)行線程 1 后執(zhí)行的情況。

總結: 調用 start 方法的順序并不能決定真正線程執(zhí)行的順序。

注意事項

start 方法會牽扯到兩個線程。

第一個就是主線程,因為我們必須要有一個主線程或者是其他的線程(哪怕不是主線程)來執(zhí)行這個 start 方法,第二個才是新的線程。

很多情況下會忽略掉為我們創(chuàng)建線程的這個主線程,不要誤以為調用了 start 就已經是子線程去執(zhí)行了,這個語句其實是主線程或者說是父線程來執(zhí)行的,被執(zhí)行之后才去創(chuàng)建新線程。

需要注意: 不能重復的執(zhí)行 start 方法

代碼示例

/**
*


* 演示不能重復的執(zhí)行 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// 即非法線程狀態(tài)異常(線程狀態(tài)不符合規(guī)定)
????????thread.start();
????}
}

報錯的原因

start 一旦開始執(zhí)行,線程狀態(tài)就從最開始的 New 狀態(tài)進入到后續(xù)的狀態(tài),比如說 Runnable,然后一旦線程執(zhí)行完畢,線程就會變成終止狀態(tài),而終止狀態(tài)永遠不可能再返回回去,所以會拋出以上異常,也就是說不能回到初始狀態(tài)了。這里描述的還不夠清晰,讓我們來看看源碼能了解的更透徹。

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".
?????*/// 第一步, 檢查線程狀態(tài)是否為初始狀態(tài), 這里也就是上面拋出異常的原因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 */
????????}
????}
}

源碼中的流程

第一步:

啟動新線程時會首先檢查線程狀態(tài)是否為初始狀態(tài), 這也是以上拋出異常的原因。即以下代碼:

if?(threadStatus != 0)throw?new?IllegalThreadStateException();

其中 threadStatus 這個變量的注釋如下,也就是說 Java 的線程狀態(tài)最初始(還沒有啟動)的時候表示為 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 接口的實現), 執(zhí)行傳入的 target 對象的 run 方法if?(target != null) {
????????target.run();
????}
}

對于 run 方法的兩種情況

第一種: 重寫了 Thread 類的 run 方法,Threadrun 方法會失效, 將會執(zhí)行重寫的 run 方法。

第二種: 傳入了 target 對象(即 Runnable 接口的實現),執(zhí)行 Thread 的原有 run 方法然后接著執(zhí)行 target 對象的 run 方法。

總結:

run 方法就是一個普通的方法, 上文中直接去執(zhí)行 run 方法也就是相當于我們執(zhí)行自己寫的普通方法一樣,所以它的執(zhí)行線程就是我們的主線程。

所以要想真正的啟動線程,不能直接調用 run 方法,而是要調用 start 方法,其中可以間接的調用 run 方法。

原文鏈接:cnblogs.com/txxunmei/p/13747631.html

總結

以上是生活随笔為你收集整理的线程组多次调用_详细分析 Java 中启动线程的正确和错误方式的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

歡迎分享!

轉載請說明來源于"生活随笔",并保留原作者的名字。

本文地址:线程组多次调用_详细分析 Java 中启动线程的正确和错误方