Thread啓動線程的start方法能執行屢次嗎?

線程的建立

咱們知道在Java裏線程是經過java.lang.Thread類來實現的。通常咱們建立無返回值的線程會用下面兩個方法:java

  1. 繼承Thread類,重寫run()方法;
  2. 實現Runnable接口,重寫run()方法;

線程啓動會經過調用start方法來啓動線程而不能直接調用run方法。面試

這裏就會引出兩個經典的面試題:markdown

  1. 爲何線程啓動是調用start方法來啓動線程而不能直接調用run方法?
  2. 若是屢次調用start方法會發生什麼?

其實答案就是源碼裏,在這以前咱們要了解線程的狀態有哪些。多線程

線程的狀態

線程從建立到死亡是會經歷多個狀態的流轉的。它們分別是:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATEDide

Java-Thread-Status
Java-Thread-Status
// Thread類的內部的枚舉類定義了線程的狀態
public enum State {  NEW,  RUNNABLE,  BLOCKED,  WAITING,  TIMED_WAITING,  TERMINATED; } 複製代碼

start() 方法 & run()方法

爲何線程啓動是調用start方法來啓動線程而不能直接調用run方法?

從上圖中,咱們就能夠看到,在New了一個線程後,首先進入初始態,而後調用start()方法來到就緒態,這裏並不會當即執行線程裏的任務,而是會等待系統資源分配,當分配到時間片後就能夠開始運行了。 start()方法是一個native方法,它將啓動一個新線程,並執行run()方法,這是真正的多線程工做。 而直接執行 run() 方法,會把 run 方法當成一個 main 線程下的普通方法去執行,並不會在某個線程中執行它,因此這並非多線程工做。這就是爲何調用 start() 方法時會執行 run() 方法,爲何不能直接調用 run() 方法的緣由了。oop

Example:測試

public class ThreadDemo  public static void main(String[] args) {  Thread t1 = new Thread(new Task1());  Thread t2 = new Thread(new Task2());   // 測試1  t1.start();  t2.start();   // 測試2  t1.run();  t2.run();   } }  class Task1 implements Runnable {   @Override  public void run() {  for (int i = 0; i < 10; i++) {  System.out.println("Task1: " + i);  try {  Thread.sleep(100);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  } }  class Task2 implements Runnable {   @Override  public void run() {  for (int i = 10; i > 0; i--) {  System.out.println("Task2: " + i);  try {  Thread.sleep(100);  } catch (InterruptedException e) {  e.printStackTrace();  }  }  } }   // 測試1輸出 Task1: 0 Task2: 10 Task1: 1 Task2: 9 Task1: 2 Task2: 8 Task1: 3 Task2: 7 Task1: 4 Task2: 6 Task1: 5 Task2: 5 Task1: 6 Task2: 4 Task1: 7 Task2: 3 Task1: 8 Task2: 2 Task1: 9 Task2: 1 咱們能夠看到Task1 和 Task2是交替打印的,是多線程在運行。 // 測試2輸出 Task1: 0 Task1: 1 Task1: 2 Task1: 3 Task1: 4 Task1: 5 Task1: 6 Task1: 7 Task1: 8 Task1: 9 Task2: 10 Task2: 9 Task2: 8 Task2: 7 Task2: 6 Task2: 5 Task2: 4 Task2: 3 Task2: 2 Task2: 1 這個的輸出是串行的,Task1 執行完才執行 Task2,因此不是多線程,是普通方法。 複製代碼

若是屢次調用start方法會發生什麼?

首先咱們先測試一下。this

Example:spa

public class ThreadDemo  public static void main(String[] args) {  Thread t1 = new Thread(new Task1());  Thread t2 = new Thread(new Task2());   // 測試3  t1.start();  t1.start();  } }  // 測試3輸出 Task1: 0 Task... Exception in thread "main" java.lang.IllegalThreadStateException  at java.lang.Thread.start(Thread.java:708) Task... 複製代碼

只有第一次成功執行,後面就拋出了異常java.lang.IllegalThreadStateException,讓咱們來從下面的源碼裏看看吧,在start方法進來後就會判斷線程的狀態,若是不是初始態狀態就會拋出異常,因此第二次執行就會報錯,由於線程的狀態已經發生改變。線程

源碼

start()方法源碼:

public synchronized void start() {  
 // 若是線程不是"NEW狀態",則拋出異常!   if (threadStatus != 0)  throw new IllegalThreadStateException();  // 將線程添加到ThreadGroup中   group.add(this);  boolean started = false;  try {  // 經過start0()啓動線程,新線程會調用run()方法   start0();  // 設置started標記=true   started = true;  } finally {  try {  if (!started) {  group.threadStartFailed(this);  }  } catch (Throwable ignore) {  }  }  } 複製代碼

run方法源碼:

public void run() {  
 if (target != null) {  target.run();  } } 複製代碼

總結

start()方法是用來啓動線程,真正實現了多線程運行。

run()方法是一個普通方法。

調用start()方法後會先判斷線程的狀態是否爲NEW,因此線程只能啓動一次。


求關注、分享、在看!!! 你的支持是我創做最大的動力。

本文使用 mdnice 排版

相關文章
相關標籤/搜索