妹子問(wèn)我為啥啟動(dòng)線程時(shí)使用 Start 而不是 Run
本文轉(zhuǎn)載自微信公眾號(hào)「Java極客技術(shù)」,作者鴨血粉絲。轉(zhuǎn)載本文請(qǐng)聯(lián)系Java極客技術(shù)公眾號(hào)。
今天團(tuán)隊(duì)里面的妹子問(wèn)阿粉,為什么在啟動(dòng)線程的時(shí)候,都使用 start 方法,而不是 run 方法呢
還好阿粉平時(shí)一直有在學(xué)習(xí),要不真的被妹子問(wèn)住了
在多線程中,如果想讓一個(gè)線程啟動(dòng),你使用的方法一定是 thread.start() 方法,而不是 thread.run() 方法(啥,你用的不是 thread.start() 方法?乖,你的打開(kāi)方式不對(duì)哦,下次不要這樣了
有沒(méi)有疑惑,為什么每次我們都習(xí)慣調(diào)用 start() 方法,為什么不直接調(diào)用 run() 方法來(lái)啟動(dòng)線程呢?
而且如果去看源碼的話,你會(huì)發(fā)現(xiàn),在 thread.start() 方法中,其實(shí)最后還是調(diào)用了 thread.run() 方法來(lái)執(zhí)行
- Causes this thread to begin execution; the Java Virtual Machine
- calls the <code>run</code> method of this thread.
上面的注釋翻譯一下:當(dāng)線程開(kāi)始執(zhí)行時(shí), JVM 會(huì)調(diào)用此線程的 run 方法
也就是說(shuō),線程的 run 方法是由 JVM 直接調(diào)用的,在 Java 中如果我們想要直接調(diào)用 run 方法也是可以的,因?yàn)樵?Java 中 run 方法是 public 的
- @Override
- public void run() {
- if (target != null) {
- target.run();
- }
- }
那既然 start 方法最后調(diào)用的也是 run 方法,再加上 run 方法本身支持直接調(diào)用,那為啥我們平時(shí)寫(xiě)的程序都是調(diào)用 start 方法,而不是 run 方法呢
那是因?yàn)?,如果直接調(diào)用 run 方法,就不是多線程了
為了方便解釋?zhuān)蹅兛磦€(gè)小 demo :
- public class RunThread {
- public static void main(String[] args) {
- Thread runThread = new Thread(new Runnable() {
- @Override
- public void run() {
- System.out.printf("Run begin another , 當(dāng)前線程 : %s.%n" ,Thread.currentThread().getName());
- }
- });
- // 啟動(dòng)線程
- runThread.start();
- // 直接調(diào)用 run 方法 -- 演示使用,實(shí)際中不要這么做!
- runThread.run();
- System.out.printf("Run begin , 當(dāng)前線程 : %s.%n" ,Thread.currentThread().getName());
- }
- }
上面的程序運(yùn)行結(jié)果如下:
你會(huì)發(fā)現(xiàn), runThread 的 run 方法被執(zhí)行了兩次
一次是 run 方法運(yùn)行在自己的線程中,從 Run begin another , 當(dāng)前線程 : Thread-0 可以看到,這個(gè)線程是運(yùn)行在 Thread-0 中
另外一次是因?yàn)槲覀兊某绦虼a直接調(diào)用了 run 方法,此時(shí)的線程運(yùn)行在 main 線程中,從 Run begin another , 當(dāng)前線程 : main 可以看出來(lái)
也就是說(shuō),如果我們直接調(diào)用 run 方法的話,線程并不是運(yùn)行在自己的線程中,而是運(yùn)行在了當(dāng)前線程中
我們?yōu)槭裁匆獎(jiǎng)?chuàng)建多線程?不就是希望多個(gè)線程并行執(zhí)行,比如現(xiàn)在我是線程 A ,此時(shí)又起了一個(gè)線程,那么我希望這個(gè)線程是和線程 A 一起運(yùn)行的,如果直接調(diào)用了 run 方法的話,就運(yùn)行在線程 A 里面來(lái)了
并沒(méi)有達(dá)到創(chuàng)建多線程的目標(biāo),這怎么行呢,對(duì)不對(duì)
所以在啟動(dòng)線程時(shí),都是使用 start 方法,而不是 run 方法
這一點(diǎn),其實(shí)在源碼中也有說(shuō)明:
the Java Virtual Machine calls the run method of this thread.
The result is that two threads are running concurrently:
the current thread (which returns from the call to the start method)
and the other thread (which executes its run method).
在 JVM 調(diào)用線程的 run 方法之后,結(jié)果就是兩個(gè)線程同時(shí)運(yùn)行:
- 當(dāng)前線程(從調(diào)用返回到 start 方法)
- 另一個(gè)線程(執(zhí)行 run 方法)
一個(gè)線程能不能 start 兩次?
妹子搞懂了為什么線程一般都使用 start 方法,不使用 run 方法,因?yàn)檎{(diào)用的話,就違背了我們想要用多線程去處理的初衷
妹子又問(wèn)阿粉,那一個(gè)線程是不是可以 start 兩次呢?
負(fù)責(zé)任的阿粉趕緊告訴小妹妹是不可以的
如果一個(gè)線程啟動(dòng)兩次,想都不用想會(huì)拋出 IllegalThreadStateException 這個(gè)異常
這個(gè)錯(cuò)誤我們也能在源碼中看到:
- if (threadStatus != 0)
- throw new IllegalThreadStateException();
線程開(kāi)始 start 時(shí),會(huì)首先判斷 threadStatus 的值是否為 0 ,如果值不是 0 的話,說(shuō)明這個(gè)線程的 state 狀態(tài)不是 new 就拋出 IllegalThreadStateException 異常
啊?竟然還想問(wèn)阿粉,線程的狀態(tài)除了 new 還有什么?阿粉以前寫(xiě)過(guò)一篇,要不要看看:面試官?zèng)]想到,一個(gè) Java 線程生命周期,我可以扯半小時(shí)