Java 多线程启动为什么调用 start() 方法而不是 run() 方法?
點擊上方?好好學java?,選擇?星標?公眾號
重磅資訊、干貨,第一時間送達 今日推薦:終于放棄了單調的swagger-ui了,選擇了這款神器—knife4j個人原創100W+訪問量博客:點擊前往,查看更多多線程在工作中多多少少會用到,啟動多線程調用的是 start() 方法,而不是 run() 方法,這是為什么呢?
在探討這個問題之前,先來了解(復習)一些多線程的基礎知識~
線程的狀態
Java 中,定義了 6 種線程狀態,在 Thread 類可以找到:
//?為了節約空間,我刪除了注釋 public?enum?State?{NEW,//初始狀態RUNNABLE,//運行狀態BLOCKED,//?阻塞狀態WAITING,//等待狀態TIMED_WAITING,//超時等待狀態TERMINATED;//終止狀態}這 6 種狀態之間的關聯,可以看下面這張圖:
圖片來源網絡附上我歷時三個月總結的?Java面試思維導圖,拿去不謝!
下載方式
1.?首先掃描下方二維碼
2.?后臺回復「思維導圖」即可獲取
這張圖描述的還是非常詳細的,結合這張圖,來說說這幾種狀態分別代表著什么意思:
-
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{@Overridepublic?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()?{@Overridepublic?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?thenit?will?be?passed?up?the?call?stack?*/}} }start 方法的源碼也沒幾行代碼,注釋也比較詳細,最主要的是 start0() 方法,這個后面在解釋。再來看看 run() 方法的源碼:
????@Overridepublic?void?run()?{//?簡單的運行,不會新起線程,target?是?Runnableif?(target?!=?null)?{target.run();}}run() 方法的源碼就比較簡單的,就是一個普通方法的調用,這也印證了我們上面的結論。
接下來我們就來說一說這個 start0() 這個方法,這個是真正實現多線程的關鍵,start0() 代碼如下:
private?native?void?start0();start0 被標記成 native ,也就是本地方法,并不需要我們去實現或者了解,**為什么 start0() 會標記成 native **?
這個要從 Java 跨平臺說起,看下面這張圖:
圖片來源??途Wstart() 方法調用 start0() 方法后,該線程并不一定會立馬執行,只是將線程變成了可運行狀態(NEW ---> RUNNABLE)。具體什么時候執行,取決于 CPU ,由 CPU 統一調度。
我們又知道 Java 是跨平臺的,可以在不同系統上運行,每個系統的 CPU 調度算法不一樣,所以就需要做不同的處理,這件事情就只能交給 JVM 來實現了,start0() 方法自然就表標記成了 native。
最后,總結一下,Java 中實現真正的多線程是 start 中的 start0() 方法,run() 方法只是一個普通的方法。
最后,再附上我歷時三個月總結的?Java 面試 + Java 后端技術學習指南,這是本人這幾年及春招的總結,目前,已經拿到了大廠offer,拿去不謝!
下載方式
1.?首先掃描下方二維碼
2.?后臺回復「Java面試」即可獲取
總結
以上是生活随笔為你收集整理的Java 多线程启动为什么调用 start() 方法而不是 run() 方法?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 百度 71个炸天的开源项目,你知道几个?
- 下一篇: volatile 手摸手带你解析