1. 程式人生 > >Thread 的start()與run()的區別與聯絡

Thread 的start()與run()的區別與聯絡

相關概念

  • 程序:程序是具有一定獨立功能的程式關於某個資料集合上的一次執行活動;程序是系統進行資源分配和排程的一個獨立單位
  • 執行緒:執行緒是程序的一個實體,是CPU排程和分派的基本單位,它是比程序更小的能獨立執行的基本單位.執行緒自己基本上不擁有系統資源,只擁有一點在執行中必不可少的資源(如程式計數器,一組暫存器和棧),但是它可與同屬一個程序的其他的執行緒共享程序所擁有的全部資源
  • 多執行緒:一個程式或程序中包含多個執行緒
  • 並行:多個cpu例項或者多臺機器同時執行一段處理邏輯,是真正的同時
  • 併發:通過cpu排程演算法,讓使用者看上去同時執行,實際上從cpu操作層面不是真正的同時
  • 多執行緒機制:將CPU排程時間分為多個時間片,採用時間片輪詢,即每個執行緒只執行一個時間片的時間,再呼叫下一個執行緒執行;即巨集觀上所有執行緒併發執行
  • java.lang.Thread:java 中通過 Thread 模擬執行緒實現,實現Runnable 介面
  • start():作用啟動執行緒,實現多執行緒執行;執行緒啟動後無需等待當前執行緒的方法體 run() 方法執行完畢,可繼續執行 start() 後的程式碼;呼叫 start() 方法後執行緒處於就緒狀態(可執行狀態),並沒有執行,等待VM呼叫,一旦得到時間片,開始執行 run() 方法體中的內容(執行緒需要完成的任務),直至執行緒執行結束(若出現阻塞,則等待喚醒再次進入就緒狀態,等待CPU的再次呼叫)
  • run():執行緒的方法體,方法內容為執行緒需要執行的內容,執行緒執行時通過呼叫run()方法完成執行的內容;如果直接呼叫 run() 方法,程式中依然只有主執行緒這一個執行緒(方法在主執行緒中執行),其程式執行路徑還是隻有一條,還是要順序執行,還是要等待run方法體執行完畢後才可繼續執行下面的程式碼,這樣就沒有達到寫執行緒的目的(執行緒的目的是併發而不是序列)
  • 聯絡:start()方法啟動執行緒將自動呼叫 run()方法,這是由jvm的記憶體機制規定的。並且run()方法必須是public訪問許可權,返回值型別為void.
  • 執行緒的建立方式
// 1. 繼承 Thread ,此處 Thread 是一個類,Thread 類實現 Runnable 介面:
// 子類通過覆蓋重寫父類中的 run 方法體中的內容實現當前寫成需要完成的任務
public class ThreadTest extends Thread{

    @Override
    public void run() {
        super.run();
    }
}
// 2.實現 Runnable 介面,需實現 run () 方法
public class RunnableTest implements Runnable{ public void run() { } }
  • 執行緒的呼叫方式
// 1.繼承 Thread 類
    public static void main(String[] args) {
        ThreadTest test = new ThreadTest();
        test.start();

    }
// 2.實現 Runnable 介面
    public static void main(String[] args) {
        RunnableTest test = new RunnableTest();
        Thread thread = new Thread(test);
        thread.start();
    }
  • 執行緒的狀態及執行緒狀態間的轉換
    建立(new Thread()) –>
    就緒(執行start()方法後,等待CPU排程)–>
    執行 ( CPU執行)–>
    阻塞 ( sleep wait 等,等待資源喚醒,重進進入就緒佇列)–>
    結束(執行結束)

執行緒間狀態切換

為什麼不直接執行run()方法,而要執行start()方法

一、從多執行緒的實現原理分析

  • 多執行緒是為了實現執行緒間併發,提高CPU的使用率
  • 直接執行 run() 方法,在主執行緒中呼叫 Thread 的 run() 方法,等該 Thread 的 run() 方法執行完畢後才會繼續執行之後的方法,是邏輯上的序列,而沒有實現執行緒的併發
  • 執行 start() 則是啟動當前的執行緒,不是立即執行執行緒中的內容,而是讓執行緒處於就緒狀態,等VM(CPU的排程),執行緒得到時間片會自動執行 run() 方法中的內容;執行執行緒的 start() 可以繼續執行呼叫位置之後的程式碼,從而實現啟動多個執行緒的目的,實現併發

二、從原始碼實現中分析

    /**
     * Thread start() jdk.1.8.0 原始碼
     */
    /**
     * Causes this thread to begin execution; the Java Virtual Machine
     * calls the <code>run</code> method of this thread.
     * <p>
     * The result is that two threads are running concurrently: the
     * current thread (which returns from the call to the
     * <code>start</code> method) and the other thread (which executes its
     * <code>run</code> method).
     * <p>
     * It is never legal to start a thread more than once.
     * In particular, a thread may not be restarted once it has completed
     * execution.
     *
     * @exception  IllegalThreadStateException  if the thread was already
     *               started.
     * @see        #run()
     * @see        #stop()
     */
    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();
            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 */
            }
        }
    }
    // 呼叫了 native 的 start0 方法啟動執行緒
    private native void start0(); // native 本地方法

方法註釋

  • start 是執行緒開始執行的原因;JVM 呼叫當前執行緒的 run 方法
  • 結果是兩個執行緒同時執行:
  • 當前執行緒(呼叫 start方法獲取響應 )
  • 另一個執行緒(執行當前執行緒的run方法 )
    /**
     * Thread run() jdk.1.8.0 原始碼
     */
    /**
     * If this thread was constructed using a separate
     * <code>Runnable</code> run object, then that
     * <code>Runnable</code> object's <code>run</code> method is called;
     * otherwise, this method does nothing and returns.
     * <p>
     * Subclasses of <code>Thread</code> should override this method.
     *
     * @see     #start()
     * @see     #stop()
     * @see     #Thread(ThreadGroup, Runnable, String)
     */
    @Override
    public void run() {
        if (target != null) {
            target.run();
        }
    }
    /* What will be run. */
    private Runnable target;
    // 從原始碼上只是執行了當前 target 物件的run() ,而沒有併發的相關實現

方法註釋

  • 如果該執行緒是使用獨立的 Runnable 的 run 物件,那麼 Runnable物件的run方法被呼叫;
  • 否則,當前的方法什麼都不做,直接返回;
  • 子類必須重寫 run 方法;

總結

  • target是一個Runnable物件;
  • run()就是直接呼叫Thread執行緒的Runnable成員的run()方法,並不會新建一個執行緒
    即 start 方法新建一個執行緒來呼叫當前執行緒的run方法

參考資料