Java中為什么start方法不能重復(fù)調(diào)用而run方法可以?
初學(xué)線程時(shí),總是將 run 方法和 start 方法搞混,雖然二者是完全不同的兩個(gè)方法,但剛開始使用時(shí)很難分清,原因就是因?yàn)槌醮问褂脮r(shí)效果貌似是一樣的,
如下代碼所示:
public static void main(String[] args) { // 創(chuàng)建線程一 Thread thread = new Thread(new Runnable() { @Override public void run() { System.out.println("執(zhí)行線程一"); } }); // 調(diào)用 run 方法 thread.run(); // 創(chuàng)建線程二 Thread thread2 = new Thread(new Runnable() { @Override public void run() { System.out.println("執(zhí)行線程二"); } }); // 調(diào)用 start 方法 thread2.start(); }
以上程序的執(zhí)行結(jié)果如下:
從上述結(jié)果可以看出,二者調(diào)用之后的執(zhí)行效果都是一樣,都可以成功執(zhí)行任務(wù)。但是,如果在執(zhí)行線程的時(shí)候,加上打印當(dāng)前線程的名稱就能看出二者的不同了,
如下代碼所示:
public static void main(String[] args) { // 創(chuàng)建線程一 Thread thread = new Thread(new Runnable() { @Override public void run() { // 獲取到當(dāng)前執(zhí)行線程 Thread currThread = Thread.currentThread(); System.out.println("執(zhí)行線程一,線程名:" + currThread.getName()); } }); // 調(diào)用 run 方法 thread.run(); // 創(chuàng)建線程二 Thread thread2 = new Thread(new Runnable() { @Override public void run() { // 獲取到當(dāng)前執(zhí)行線程 Thread currThread = Thread.currentThread(); System.out.println("執(zhí)行線程二,線程名:" + currThread.getName()); } }); // 調(diào)用 start 方法 thread2.start(); }
以上程序的執(zhí)行結(jié)果如下:
從上述結(jié)果我們可以看出:當(dāng)調(diào)用 run 方法時(shí),其實(shí)是調(diào)用當(dāng)前主程序 main 來(lái)執(zhí)行方法體的;而調(diào)用 start 方法才是真正的創(chuàng)建一個(gè)新線程來(lái)執(zhí)行任務(wù)。
區(qū)別1
run 方法和 start 方法的第一個(gè)區(qū)別是:調(diào)用 start 方法是真正開啟一個(gè)線程來(lái)執(zhí)行任務(wù),而調(diào)用 run 方法相當(dāng)于執(zhí)行普通方法 run,并不會(huì)開啟新線程,
如下圖所示:
區(qū)別2
run 方法和 start 方法的第二個(gè)區(qū)別是:run 方法也叫做線程體,它里面包含了具體要執(zhí)行的業(yè)務(wù)代碼,當(dāng)調(diào)用 run 方法時(shí),會(huì)立即執(zhí)行 run 方法中的代碼(如果當(dāng)前線程時(shí)間片未用完);而調(diào)用 start 方法時(shí),是啟動(dòng)一個(gè)線程并將線程的狀態(tài)設(shè)置為就緒狀態(tài)。也就是說(shuō)調(diào)用 start 方法,并不會(huì)立即執(zhí)行。
區(qū)別3
因?yàn)?run 方法是普通方法,而普通方法是可以被多次調(diào)用的,所以 run 方法可以被調(diào)用多次;而 start 方法是創(chuàng)建新線程來(lái)執(zhí)行任務(wù),因?yàn)榫€程只能被創(chuàng)建一次,所以它們的第三個(gè)區(qū)別是:run 方法可以被調(diào)用多次,而 start 方法只能被調(diào)用一次。
測(cè)試代碼如下:
// 創(chuàng)建線程一 Thread thread = new Thread(new Runnable() { @Override public void run() { // 獲取到當(dāng)前執(zhí)行的線程 Thread currThread = Thread.currentThread(); System.out.println("執(zhí)行線程一,線程名:" + currThread.getName()); } }); // 調(diào)用 run 方法 thread.run(); // 多次調(diào)用 run 方法 thread.run(); // 創(chuàng)建線程二 Thread thread2 = new Thread(new Runnable() { @Override public void run() { // 獲取到當(dāng)前執(zhí)行的線程 Thread currThread = Thread.currentThread(); System.out.println("執(zhí)行線程二,線程名:" + currThread.getName()); } }); // 調(diào)用 start 方法 thread2.start(); // 多次調(diào)用 start 方法 thread2.start();
以上程序的執(zhí)行結(jié)果如下:
從上述結(jié)果可以看出,run 方法多次調(diào)用可用正常執(zhí)行,而第二次調(diào)用 start 方法時(shí)程序就報(bào)錯(cuò)了,提示“IllegalThreadStateException”非法線程狀態(tài)異常。
為什么start不能被重復(fù)調(diào)用?
要找到這個(gè)問(wèn)題的答案,就要查看 start 方法的實(shí)現(xiàn)源碼,它的源碼如下:
從 start 源碼實(shí)現(xiàn)的第一行,我們就可以得到問(wèn)題的答案,因?yàn)?start 方法在執(zhí)行時(shí),會(huì)先判斷當(dāng)前線程的狀態(tài)是不是等于 0,也就是是否為新建狀態(tài) NEW,如果不等于新建狀態(tài),那么就會(huì)拋出“IllegalThreadStateException”非法線程狀態(tài)異常,這就是線程的 start 方法不能被重復(fù)調(diào)用的原因。 它的執(zhí)行過(guò)程是:當(dāng)線程調(diào)用了第一個(gè) start 方法之后,線程的狀態(tài)就會(huì)從新建狀態(tài) NEW,變?yōu)榫途w狀態(tài) RUNNABLE,此時(shí)再次調(diào)用 start 方法,JVM 就會(huì)判斷出當(dāng)前的線程已經(jīng)不等于新建狀態(tài),從而拋出 IllegalThreadStateException 非法線程狀態(tài)異常。
總結(jié)
run 方法和 start 方法的主要區(qū)別如下:
- 方法性質(zhì)不同:run 是一個(gè)普通方法,而 start 是開啟新線程的方法。
- 執(zhí)行速度不同:調(diào)用 run 方法會(huì)立即執(zhí)行任務(wù),調(diào)用 start 方法是將線程的狀態(tài)改為就緒狀態(tài),不會(huì)立即執(zhí)行。
- 調(diào)用次數(shù)不同:run 方法可以被重復(fù)調(diào)用,而 start 方法只能被調(diào)用一次。
start 方法之所以不能被重復(fù)調(diào)用的原因是,線程的狀態(tài)是不可逆的,Thread 在 start 的實(shí)現(xiàn)源碼中做了判斷,如果線程不是新建狀態(tài) NEW,則會(huì)拋出非法線程狀態(tài)異常 IllegalThreadStateException。?
到此這篇關(guān)于Java中為什么start方法不能重復(fù)調(diào)用而run方法可以?的文章就介紹到這了,更多相關(guān)start與run方法內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java后綴數(shù)組之求sa數(shù)組的實(shí)例代碼
后綴數(shù)組就是一個(gè)字符串所有后綴大小排序后的一個(gè)集合,然后我們根據(jù)后綴數(shù)組的一些性質(zhì)就可以實(shí)現(xiàn)各種需求。這篇文章主要介紹了Java后綴數(shù)組-求sa數(shù)組,需要的朋友可以參考下2018-04-04MybatisPlus調(diào)用原生SQL的三種方法實(shí)例詳解
這篇文章主要介紹了MybatisPlus調(diào)用原生SQL的三種方法,在有些情況下需要用到MybatisPlus查詢?cè)鶶QL,MybatisPlus其實(shí)帶有運(yùn)行原生SQL的方法,我這里列舉三種,需要的朋友可以參考下2022-09-09淺談java字符串比較到底應(yīng)該用==還是equals
這篇文章主要介紹了淺談java字符串比較到底應(yīng)該用==還是equals,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12Mybatis持久層框架入門之CRUD實(shí)例代碼詳解
這篇文章主要介紹了Mybatis持久層框架入門之CRUD實(shí)例,需要的朋友可以參考下2022-05-05完美解決gson將Integer默認(rèn)轉(zhuǎn)換成Double的問(wèn)題
下面小編就為大家?guī)?lái)一篇完美解決gson將Integer默認(rèn)轉(zhuǎn)換成Double的問(wèn)題。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-03-03Java實(shí)現(xiàn)HashMap排序方法的示例詳解
這篇文章主要通過(guò)一些示例為大家介紹了Java對(duì)HashMap進(jìn)行排序的方法,幫助大家更好的理解和使用Java,感興趣的朋友可以了解一下2022-05-05eclipse修改jvm參數(shù)調(diào)優(yōu)方法(2種)
本篇文章主要介紹了eclipse修改jvm參數(shù)調(diào)優(yōu)方法(2種),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02