程式碼示範:##
/** * <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 方法可以啟動一個新執行緒。
方法之後, 當前線程(通常是主線程)會請求JVM 虛擬機器如果有空閒的話來啟動一下這邊的這個新線程。
方法,也不一定能夠立刻的啟動執行緒。
方法呼叫之後,並不意味著這個方法已經開始運作了。它可能稍後才會運行,也很有可能很長時間都不會運行,比如說遇到了飢餓的情況。
方法,而線程2 後調用了
start 方法,卻發現線程2 先執行線程1 後執行的情況。
方法的順序並不能決定真正執行緒執行的順序。
方法會牽扯到兩個執行緒。
方法,第二個才是新的線程。
就已經是子線程去執行了,這個語句其實是主線程或者說是父線程來執行的,執行之後才去建立新線程。
start 方法建立新執行緒的準備工作
# 方法中的代碼。
需要注意: 不能重複的執行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(); } }复制代码
一旦開始執行,執行緒狀態就從最開始的New 狀態進入到後續的狀態,比如說Runnable,然後一旦執行緒執行完畢,執行緒就會變成終止狀態,而終止狀態永遠不可能再回回去,所以會拋出以上異常,也就是說不能回到初始狀態了。這裡描述的還不夠清晰,讓我們來看看原始碼能了解的更透徹。
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 方法原始碼分析
@Overridepublic void run() { // 传入了 target 对象(即 Runnable 接口的实现), 执行传入的 target 对象的 run 方法 if (target != null) { target.run(); } }复制代码
Thread 類別的
run 方法,
Thread 的
run 方法會失效, 將會執行重寫的
run 方法。
第二種: 傳入了target
物件(即Runnable
介面的實作),執行Thread
的原有run
方法接著接著執行target
物件的run
方法。
總結:
run
方法就是一個普通的方法, 上文直接去執行run
方法也就是相當於我們執行自己寫的普通方法一樣,所以它的執行緒就是我們的主執行緒。 run
方法,而是要呼叫start
方法,可以間接的呼叫run
方法。 相關學習推薦:#java基礎
以上是細品 Java 中啟動執行緒的正確和錯誤方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!