搜尋
首頁JavaJava基礎細品 Java 中啟動執行緒的正確和錯誤方式

細品 Java 中啟動執行緒的正確和錯誤方式

細品Java 中啟動執行緒的正確與錯誤方式

前文回顧

  1. 詳細分析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&#39;s list of threads
     * and the group&#39;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 &#39;not yet started&#39;
 */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();
    }
}复制代码

對於run 方法的兩種情況

  • 第一種: 重寫了

    Thread 類別的run 方法,Threadrun 方法會失效, 將會執行重寫的run 方法。

  • 第二種: 傳入了target 物件(即Runnable 介面的實作),執行Thread 的原有run 方法接著接著執行target 物件的run 方法。

  • 總結:

    • run 方法就是一個普通的方法, 上文直接去執行run 方法也就是相當於我們執行自己寫的普通方法一樣,所以它的執行緒就是我們的主執行緒。
    • 所以要想真正的啟動線程,不能直接呼叫run 方法,而是要呼叫start 方法,可以間接的呼叫run 方法。

相關學習推薦:#java基礎

以上是細品 Java 中啟動執行緒的正確和錯誤方式的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:juejin。如有侵權,請聯絡admin@php.cn刪除

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

PhpStorm Mac 版本

PhpStorm Mac 版本

最新(2018.2.1 )專業的PHP整合開發工具