# Java線程(四):線程中斷、線程讓步、線程睡眠、線程合并
最近在Review線程專欄,修改了諸多之前描述不夠嚴謹的地方,凡是帶有Review標記的文章都是修改過了。本篇文章是插進來的,因為原來沒有寫,現在來看傳統線程描述的不太完整,所以就補上了。理解了線程同步和線程通信之后,再來看本文的知識點就會簡單的多了,本文是做為傳統線程知識點的一個補充。有人會問:JDK5之后有了更完善的處理多線程問題的類(并發包),我們還需要去了解傳統線程嗎?答:需要。在實際開發中,無外乎兩種情況,一個是開發新內容,另一個是維護原有程序。開發新內容可以使用新的技術手段,但是我們不能保證原有程序是用什么實現的,所以我們需要了解原有的。另外一點,了解傳統線程的工作原理,使我們在使用并發包時更加得心應手。
## 線程中斷
線程中斷涉及到三個方法,如下:
| `void` | `[interrupt](http://blog.csdn.net/ghsau/article/details/17560467)**()`中斷線程。 |
|---|---|---|
| `static?boolean` | `[interrupted](http://blog.csdn.net/ghsau/article/details/17560467)**()`測試當前線程是否已經中斷。 |
| `boolean` | `[isInterrupted](http://blog.csdn.net/ghsau/article/details/17560467)**()`測試線程是否已經中斷。 |
interrupt()方法用于中斷線程,通常的理解來看,只要某個線程啟動后,調用了該方法,則該線程不能繼續執行了,來看個小例子:
~~~
public?class?InterruptTest?{??
????public?static?void?main(String[]?args)?throws?InterruptedException?{??
????????MyThread?t?=?new?MyThread("MyThread");??
????????t.start();??
????????Thread.sleep(100);//?睡眠100毫秒??
????????t.interrupt();//?中斷t線程??
????}??
}??
class?MyThread?extends?Thread?{??
????int?i?=?0;??
????public?MyThread(String?name)?{??
????????super(name);??
????}??
????public?void?run()?{??
????????while(true)?{//?死循環,等待被中斷??
????????????System.out.println(getName()?+?getId()?+?"執行了"?+?++i?+?"次");??
????????}??
????}??
}??
~~~
運行后,我們發現,線程t一直在執行,沒有被中斷,原來interrupt()是騙人的,汗!其實interrupt()方法并不是中斷線程的執行,而是為調用該方法的線程對象打上一個標記,設置其中斷狀態為true,通過isInterrupted()方法可以得到這個線程狀態,我們將上面的程序做一個小改動:
~~~
public?class?InterruptTest?{??
????public?static?void?main(String[]?args)?throws?InterruptedException?{??
????????MyThread?t?=?new?MyThread("MyThread");??
????????t.start();??
????????Thread.sleep(100);//?睡眠100毫秒??
????????t.interrupt();//?中斷t線程??
????}??
}??
class?MyThread?extends?Thread?{??
????int?i?=?0;??
????public?MyThread(String?name)?{??
????????super(name);??
????}??
????public?void?run()?{??
????????while(!isInterrupted())?{//?當前線程沒有被中斷,則執行??
????????????System.out.println(getName()?+?getId()?+?"執行了"?+?++i?+?"次");??
????????}??
????}??
}??
~~~
這樣的話,線程被順利的中斷執行了。很多人實現一個線程類時,都會再加一個flag標記,以便控制線程停止執行,其實完全沒必要,通過線程自身的中斷狀態,就可以完美實現該功能。如果線程在調用 Object 類的 wait()、wait(long) 或 wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法過程中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。 我們可以捕獲該異常,并且做一些處理。另外,Thread.interrupted()方法是一個靜態方法,它是判斷當前線程的中斷狀態,需要注意的是,線程的中斷狀態會由該方法清除。換句話說,如果連續兩次調用該方法,則第二次調用將返回 false(在第一次調用已清除了其中斷狀態之后,且第二次調用檢驗完中斷狀態前,當前線程再次中斷的情況除外)。
## 線程讓步
線程讓步,其方法如下:
| `static?void` | `[yield](http://blog.csdn.net/ghsau/article/details/17560467)**()`暫停當前正在執行的線程對象,并執行其他線程 |
|---|---|---|
線程讓步用于正在執行的線程,在某些情況下讓出CPU資源,讓給其它線程執行,來看一個小例子:
~~~
public?class?YieldTest?{??
????public?static?void?main(String[]?args)?throws?InterruptedException?{??
????????//?創建線程對象??
????????YieldThread?t1?=?new?YieldThread("t1");??
????????YieldThread?t2?=?new?YieldThread("t2");??
????????//?啟動線程??
????????t1.start();??
????????t2.start();??
????????//?主線程休眠100毫秒??
????????Thread.sleep(100);??
????????//?終止線程??
????????t1.interrupt();??
????????t2.interrupt();??
????}??
}??
class?YieldThread?extends?Thread?{??
????int?i?=?0;??
????public?YieldThread(String?name)?{??
????????super(name);??
????}??
????public?void?run()?{??
????????while(!isInterrupted())?{??
????????????System.out.println(getName()?+?"執行了"?+?++i?+?"次");??
????????????if(i?%?10?==?0)?{//?當i能對10整除時,則讓步??
????????????????Thread.yield();??
????????????}??
????????}??
????}??
}??
~~~
輸出結果略,從輸出結果可以看到,當某個線程(t1或者t2)執行到10次、20次、30次等時,就會馬上切換到另一個線程執行,接下來再交替執行,如此往復。**注意,如果存在synchronized線程同步的話,線程讓步不會釋放鎖(監視器對象)**。
## 線程睡眠
線程睡眠涉及到兩個方法,如下:
| `static?void` | `[sleep](http://blog.csdn.net/ghsau/article/details/17560467)**(long?millis)`在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)。 |
|---|---|---|
| `static?void` | `[sleep](http://blog.csdn.net/ghsau/article/details/17560467)**(long?millis, int?nanos)`在指定的毫秒數加指定的納秒數內讓當前正在執行的線程休眠(暫停執行)。 |
**線程睡眠的過程中,如果是在synchronized線程同步內,是持有鎖(監視器對象)的**,也就是說,線程是關門睡覺的,別的線程進不來,來看一個小例子:
~~~
public?class?SleepTest?{??
????public?static?void?main(String[]?args)?{??
????????//?創建共享對象??
????????Service?service?=?new?Service();??
????????//?創建線程??
????????SleepThread?t1?=?new?SleepThread("t1",?service);??
????????SleepThread?t2?=?new?SleepThread("t2",?service);??
????????//?啟動線程??
????????t1.start();??
????????t2.start();??
????}??
??????
}??
class?SleepThread?extends?Thread?{??
????private?Service?service;??
????public?SleepThread(String?name,?Service?service)?{??
????????super(name);??
????????this.service?=?service;??
????}??
????public?void?run()?{??
????????service.calc();??
????}??
}??
class?Service?{??
????public?synchronized?void?calc()?{??
????????System.out.println(Thread.currentThread().getName()?+?"準備計算");??
????????System.out.println(Thread.currentThread().getName()?+?"感覺累了,開始睡覺");??
????????try?{??
????????????Thread.sleep(10000);//?睡10秒??
????????}?catch?(InterruptedException?e)?{??
????????????return;??
????????}??
????????System.out.println(Thread.currentThread().getName()?+?"睡醒了,開始計算");??
????????System.out.println(Thread.currentThread().getName()?+?"計算完成");??
????}??
}??
~~~
輸出結果:
~~~
t1準備計算??
t1感覺累了,開始睡覺??
t1睡醒了,開始計算??
t1計算完成??
t2準備計算??
t2感覺累了,開始睡覺??
t2睡醒了,開始計算??
t2計算完成??
~~~
## 線程合并
線程合并涉及到三個方法,如下:
| `?void` | `[join](http://blog.csdn.net/ghsau/article/details/17560467)**()`等待該線程終止。 |
|---|---|---|
| `?void` | `[join](http://blog.csdn.net/ghsau/article/details/17560467)**(long?millis)`等待該線程終止的時間最長為?`millis`?毫秒。 |
| `?void` | `[join](http://blog.csdn.net/ghsau/article/details/17560467)**(long?millis, int?nanos)`等待該線程終止的時間最長為?`millis`?毫秒 +?`nanos`?納秒。 |
線程合并是優先執行調用該方法的線程,再執行當前線程,來看一個小例子:
~~~
public?class?JoinTest?{??
????public?static?void?main(String[]?args)?throws?InterruptedException?{??
????????JoinThread?t1?=?new?JoinThread("t1");??
????????JoinThread?t2?=?new?JoinThread("t2");??
????????t1.start();??
????????t2.start();??
????????t1.join();??
????????t2.join();??
????????System.out.println("主線程開始執行!");??
????}??
}??
class?JoinThread?extends?Thread?{??
????public?JoinThread(String?name)?{??
????????super(name);??
????}??
????public?void?run()?{??
????????for(int?i?=?1;?i?10;?i++)??
????????????System.out.println(getName()?+?getId()?+?"執行了"?+?i?+?"次");??
????}??
}??
~~~
t1和t2都執行完才繼續主線程的執行,所謂合并,就是等待其它線程執行完,再執行當前線程,執行起來的效果就好像把其它線程合并到當前線程執行一樣。
## 線程優先級
線程最低優先級為1,最高優先級為10,看起來就有10個級別,但這10個級別能不能和CPU對應上,還未可知,Thread類中提供了優先級的三個常量,如下:
| java.lang.[Thread](http://blog.csdn.net/ghsau/article/details/17560467 "java.lang 中的類") |
|---|---|---|---|
| `public?static?final?int``[MAX_PRIORITY](http://blog.csdn.net/ghsau/article/details/17560467)` | `10` |
| `public?static?final?int``[MIN_PRIORITY](http://blog.csdn.net/ghsau/article/details/17560467)` | `1` |
| `public?static?final?int``[NORM_PRIORITY](http://blog.csdn.net/ghsau/article/details/17560467)` | `5` |
我們創建線程對象后,如果不顯示的設置優先級的話,默認為5。優先級可以看成一種特權,優先級高的,獲取CPU調度的機會就大,優先級低的,獲取CPU調度的機會就小,這個和我們現實生活很一樣啊,優勝劣汰。線程優先級的示例就不寫了,比較簡單。
## wait()和sleep()區別
區別太大了,但是在Java線程面試題中是很常見的問題,相信你閱讀過本專欄后,能夠輕松的解答,這里不再贅述。
- 前言
- Java線程(一):線程安全與不安全
- Java線程(二):線程同步synchronized和volatile
- Java線程(三):線程協作-生產者/消費者問題
- Java線程(四):線程中斷、線程讓步、線程睡眠、線程合并
- Java線程(五):Timer和TimerTask
- Java線程(六):線程池
- Java線程(七):Callable和Future
- Java線程(八):鎖對象Lock-同步問題更完美的處理方式
- Java線程(九):Condition-線程通信更高效的方式
- Java線程(十):CAS
- Java線程(十一):Fork/Join-Java并行計算框架
- Java線程(篇外篇):阻塞隊列BlockingQueue
- Java線程(篇外篇):線程本地變量ThreadLocal
- Java線程(篇外篇):線程和鎖