先來看下結論:正確啓動線程的方式是使用start()方法,而不是使用run()方法。html
「Talk is cheap. Show me the code」,用代碼說話:分別調用run()方法和start()方法,打印輸出線程的名字。安全
public class StartAndRunThread { public static void main(String[] args) { Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; runnable.run(); new Thread(runnable).start(); } }
運行結果:
併發
若是代碼是這樣的,執行結果有什麼不一樣呢?ide
public class StartAndRunThread { public static void main(String[] args) { Runnable runnable = new Runnable() { @Override public void run() { System.out.println(Thread.currentThread().getName()); } }; runnable.run(); new Thread(runnable).start(); runnable.run(); } }
執行結果爲:
源碼分析
是否是有點意外?然而,這就是真相。其實也不難解釋。this
咱們說的併發是什麼,併發不就是線程之間的運行互不干擾嘛?當JVM啓動的時候,建立一個mian線程來運行main()方法。當執行到「new Thread(runnable).start();」的時候main線程會新建一個Thread-0線程。main線程和Thread-0線程的執行時互不相干的,因此可能不會出現「main-Thread-0-main」的結果。操作系統
我執行了n(n>20)次,運行結果依然如上圖所示,沒有出現「main-Thread-0-main」。這是爲何呢?回憶一下線程的生命週期, Java中,線程(Thread)定義了6種狀態: NEW(新建)、RUNNABLE(可執行)、BLOCKED(阻塞)、WAITING(等待)、TIMED_WAITING(限時等待)、TERMINATED(結束)。當調用了start()方法以後,線程進入RUNNABLE狀態,RUNNABLE的意思是可運行,便可能正在執行,也可能沒有正在執行。那調用了start方法以後,何時執行呢?調用start()方法以後,咱們只是告訴JVM去執行這個線程,至於何時運行是由線程調度器來決定的。從操做系統層面,其實調用start()方法以後要去獲取操做系統的時間片,獲取到纔會執行。這個問題,能夠對比思考「 thread.start()調用以後線程會馬上執行嗎?」更多能夠參考:從源碼解讀線程(Thread)和線程池(ThreadPoolExecutor)的狀態線程
start()源碼以下:rest
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 */ } } }
能夠看到,start()方法被synchronized關鍵字修飾,保證了線程安全。啓動流程分爲下面三個步驟:code
首先會檢查線程狀態,只有threadStatus == 0(也就是線程處於NEW狀態)狀態下的線程才能繼續,不然會拋出IllegalThreadStateException。
將線程加入線程組
調用native方法——start0()方法啓動線程。
會拋出IllegalThreadStateException,具體緣由能夠用源碼和線程啓動步驟進行說明。
start()纔是真正啓動一個線程,而若是直接調用run(),那麼run()只是一個普通的方法而已,和線程的生命週期沒有任何關係。用代碼驗證一下:
public class Main implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } public static void main(String[] args) { new Main().run(); new Thread(new Main()).start(); } }
在上面代碼中,直接調用run()方法,run()只是一個普通的方法,由當前線程——main線程執行。start()纔是真正啓動一個線程——Thread0,run()方法由線程Thread0執行。
能夠看start()方法的註釋部分:
/** * 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() */
也就是說當該線程開始執行的時候,Java虛擬機會自動調用該線程的run()方法。