互聯網平頭哥的第  113  篇原創文章 

多線程在工作中多多少少會用到,啓動 多線程調用的是 start() 方法,而不是 run() 方法,這是爲什麼呢?

在探討這個問題之前,先來了解(複習)一些多線程的基礎知識~

線程的狀態

Java 中,定義了 6 種線程狀態,在 Thread 類可以找到:

// 爲了節約空間,我刪除了註釋
public enum State {
NEW,//初始狀態
RUNNABLE,//運行狀態
BLOCKED,// 阻塞狀態
WAITING,//等待狀態
TIMED_WAITING,//超時等待狀態
TERMINATED;//終止狀態
}

這 6 種狀態之間的關聯,可以看下面這張圖:

圖片來源網絡

這張圖描述的還是非常詳細的,結合這張圖,來說說這幾種狀態分別代表着什麼意思:

  • 1、 NEW 表示線程創建成功,但沒有運行 ,在 new Thread 之後,沒有 start 之前,線程都處於 NEW 狀態;

  • 2、 RUNNABLE 表示線程正在運行中 ,當我們運行 strat 方法,子線程被創建成功之後,子線程的狀態變成 RUNNABLE;

  • 3、 TERMINATED 表示線程已經運行結束 ,子線程運行完成、被打斷、被中止,狀態都會從 RUNNABLE 變成 TERMINATED;

  • 4、 BLOCKED 表示線程被阻塞 ,如果線程正好在等待獲得 monitor lock 鎖,比如在等待進入 synchronized 修飾的代碼塊或方法時,會從 RUNNABLE 變成 BLOCKED;

  • 5、 WAITING 和 TIMED_WAITING 都表示等待 ,現在在遇到 Object#wait、Thread#join、 LockSupport#park 這些方法時,線程就會等待另一個線程執行完特定的動作之後,才能結 束等待,只不過 TIMED_WAITING 是帶有等待時間的;

優先級

優先級代表線程執行的機會的大小,優先級高的可能先執行,低的可能後執行。

在 Java 源碼中,優先級從低到高分別是 1 到 10,線程默認 new 出來的優先級都是 5,源碼如下:

 /**
* The minimum priority that a thread can have.
*/

public final static int MIN_PRIORITY = 1;

/**
* The default priority that is assigned to a thread.
*/

public final static int NORM_PRIORITY = 5;

/**
* The maximum priority that a thread can have.
*/

public final static int MAX_PRIORITY = 10;

線程的創建方式

我們創建多線程有兩種方式,一種是繼承 Thread 類,另一種是實現 Runnable 接口。兩種方式的使用,如下所示:

1、繼承 Thread,成爲 Thread 的子類

public class MyThread extends Thread{
@Override
public void run() {
System.out.println("我是通過繼承 Thread 類實現的~");
}

public static void main(String[] args) {
MyThread thread = new MyThread();
// 啓動線程
thread.start();
}
}

2、實現 Runnable 接口

public class MyThread1 {
public static void main(String[] args) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我是通過 runnable 方式實現的~");
}
});
// 啓動線程
thread.start();
}
}

不管使用哪一種方式,啓動線程都是 thread.start() 方法,如果你做過實驗的話,你會發現 thread.run() 也可以執行,爲什麼就一定需要調用 thread.start() 方法呢

先說說結論: 首先通過 對象.run() 方法可以執行方法,但是不是使用的多線程的方式,就是一個普通的方法,要想實現多線程的方式,一定需要通過 對象.start() 方法

想要弄明白一個問題,最好的辦法就是從源碼入手,我們也從這兩個方法的源碼開始,先來看看 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's list of threads
* and the group's unstarted count can be decremented. */

group.add(this);
// 是否啓動的標識符
boolean started = false;
try {
// start0() 是啓動多線程的關鍵
// 這裏會創建一個新的線程,是一個 native 方法
// 執行完成之後,新的線程已經在運行了
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 方法的源碼也沒幾行代碼,註釋也比較詳細,最主要的是 start0() 方法,這個後面在解釋。再來看看 run() 方法的源碼:

    @Override
public void run() {
// 簡單的運行,不會新起線程,target 是 Runnable
if (target != null) {
target.run();
}
}

run() 方法的源碼就比較簡單的,就是一個普通方法的調用,這也印證了我們上面的結論。

接下來我們就來說一說這個 start0() 這個方法,這個是真正實現多線程的關鍵,start0() 代碼如下:

private native void start0();

start0 被標記成 native ,也就是本地方法,並不需要我們去實現或者瞭解,**爲什麼 start0() 會標記成 native **?

這個要從 Java 跨平臺說起,看下面這張圖:

圖片來源牛客網

start() 方法調用 start0() 方法後,該線程並不一定會立馬執行,只是將線程變成了可運行狀態(NEW ---> RUNNABLE)。具體什麼時候執行,取決於 CPU ,由 CPU 統一調度。

我們又知道 Java 是跨平臺的,可以在不同系統上運行,每個系統的 CPU 調度算法不一樣,所以就需要做不同的處理,這件事情就只能交給 JVM 來實現了,start0() 方法自然就表標記成了 native。

最後,總結一下,Java 中實現真正的多線程是 start 中的 start0() 方法,run() 方法只是一個普通的方法。

還可以讀

聊一聊 JUC 下的 ArrayBlockingQueue 隊列

聊一聊 JUC 下的 LinkedBlockingQueue

相關文章