Java線程(四)----線程中斷、線程讓步、線程睡眠、線程合併

 本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/17560467,轉載請註明。
       最近在Review線程專欄,修改了諸多之前描述不夠嚴謹的地方,凡是帶有Review標記的文章都是修改過了。本篇文章是插進來的,因爲原來沒有寫,現在來看傳統線程描述的不太完整,所以就補上了。理解了線程同步和線程通信之後,再來看本文的知識點就會簡單的多了,本文是做爲傳統線程知識點的一個補充。有人會問:JDK5之後有了更完善的處理多線程問題的類(併發包),我們還需要去了解傳統線程嗎?答:需要。在實際開發中,無外乎兩種情況,一個是開發新內容,另一個是維護原有程序。開發新內容可以使用新的技術手段,但是我們不能保證原有程序是用什麼實現的,所以我們需要了解原有的。另外一點,瞭解傳統線程的工作原理,使我們在使用併發包時更加得心應手。

線程中斷


       線程中斷涉及到三個方法,如下:
void interrupt()
          中斷線程。
static boolean interrupted()
          測試當前線程是否已經中斷。
boolean isInterrupted()
          測試線程是否已經中斷。
       interrupt()方法用於中斷線程,通常的理解來看,只要某個線程啓動後,調用了該方法,則該線程不能繼續執行了,來看個小例子:
[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class InterruptTest {  
  2.     public static void main(String[] args) throws InterruptedException {  
  3.         MyThread t = new MyThread("MyThread");  
  4.         t.start();  
  5.         Thread.sleep(100);// 睡眠100毫秒  
  6.         t.interrupt();// 中斷t線程  
  7.     }  
  8. }  
  9. class MyThread extends Thread {  
  10.     int i = 0;  
  11.     public MyThread(String name) {  
  12.         super(name);  
  13.     }  
  14.     public void run() {  
  15.         while(true) {// 死循環,等待被中斷  
  16.             System.out.println(getName() + getId() + "執行了" + ++i + "次");  
  17.         }  
  18.     }  
  19. }  
       運行後,我們發現,線程t一直在執行,沒有被中斷,原來interrupt()是騙人的,汗!其實interrupt()方法並不是中斷線程的執行,而是爲調用該方法的線程對象打上一個標記,設置其中斷狀態爲true,通過isInterrupted()方法可以得到這個線程狀態,我們將上面的程序做一個小改動:
[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class InterruptTest {  
  2.     public static void main(String[] args) throws InterruptedException {  
  3.         MyThread t = new MyThread("MyThread");  
  4.         t.start();  
  5.         Thread.sleep(100);// 睡眠100毫秒  
  6.         t.interrupt();// 中斷t線程  
  7.     }  
  8. }  
  9. class MyThread extends Thread {  
  10.     int i = 0;  
  11.     public MyThread(String name) {  
  12.         super(name);  
  13.     }  
  14.     public void run() {  
  15.         while(!isInterrupted()) {// 當前線程沒有被中斷,則執行  
  16.             System.out.println(getName() + getId() + "執行了" + ++i + "次");  
  17.         }  
  18.     }  
  19. }  

       這樣的話,線程被順利的中斷執行了。很多人實現一個線程類時,都會再加一個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()
          暫停當前正在執行的線程對象,並執行其他線程
       線程讓步用於正在執行的線程,在某些情況下讓出CPU資源,讓給其它線程執行,來看一個小例子:
[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class YieldTest {  
  2.     public static void main(String[] args) throws InterruptedException {  
  3.         // 創建線程對象  
  4.         YieldThread t1 = new YieldThread("t1");  
  5.         YieldThread t2 = new YieldThread("t2");  
  6.         // 啓動線程  
  7.         t1.start();  
  8.         t2.start();  
  9.         // 主線程休眠100毫秒  
  10.         Thread.sleep(100);  
  11.         // 終止線程  
  12.         t1.interrupt();  
  13.         t2.interrupt();  
  14.     }  
  15. }  
  16. class YieldThread extends Thread {  
  17.     int i = 0;  
  18.     public YieldThread(String name) {  
  19.         super(name);  
  20.     }  
  21.     public void run() {  
  22.         while(!isInterrupted()) {  
  23.             System.out.println(getName() + "執行了" + ++i + "次");  
  24.             if(i % 10 == 0) {// 當i能對10整除時,則讓步  
  25.                 Thread.yield();  
  26.             }  
  27.         }  
  28.     }  
  29. }  

       輸出結果略,從輸出結果可以看到,當某個線程(t1或者t2)執行到10次、20次、30次等時,就會馬上切換到另一個線程執行,接下來再交替執行,如此往復。注意,如果存在synchronized線程同步的話,線程讓步不會釋放鎖(監視器對象)


線程睡眠


       線程睡眠涉及到兩個方法,如下:
static void sleep(long millis)
          在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行)。
static void sleep(long millis, int nanos)
          在指定的毫秒數加指定的納秒數內讓當前正在執行的線程休眠(暫停執行)。
       線程睡眠的過程中,如果是在synchronized線程同步內,是持有鎖(監視器對象)的,也就是說,線程是關門睡覺的,別的線程進不來,來看一個小例子:
[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class SleepTest {  
  2.     public static void main(String[] args) {  
  3.         // 創建共享對象  
  4.         Service service = new Service();  
  5.         // 創建線程  
  6.         SleepThread t1 = new SleepThread("t1", service);  
  7.         SleepThread t2 = new SleepThread("t2", service);  
  8.         // 啓動線程  
  9.         t1.start();  
  10.         t2.start();  
  11.     }  
  12.       
  13. }  
  14. class SleepThread extends Thread {  
  15.     private Service service;  
  16.     public SleepThread(String name, Service service) {  
  17.         super(name);  
  18.         this.service = service;  
  19.     }  
  20.     public void run() {  
  21.         service.calc();  
  22.     }  
  23. }  
  24. class Service {  
  25.     public synchronized void calc() {  
  26.         System.out.println(Thread.currentThread().getName() + "準備計算");  
  27.         System.out.println(Thread.currentThread().getName() + "感覺累了,開始睡覺");  
  28.         try {  
  29.             Thread.sleep(10000);// 睡10秒  
  30.         } catch (InterruptedException e) {  
  31.             return;  
  32.         }  
  33.         System.out.println(Thread.currentThread().getName() + "睡醒了,開始計算");  
  34.         System.out.println(Thread.currentThread().getName() + "計算完成");  
  35.     }  
  36. }  
       輸出結果:
[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. t1準備計算  
  2. t1感覺累了,開始睡覺  
  3. t1睡醒了,開始計算  
  4. t1計算完成  
  5. t2準備計算  
  6. t2感覺累了,開始睡覺  
  7. t2睡醒了,開始計算  
  8. t2計算完成  

線程合併


       線程合併涉及到三個方法,如下:
 void join()
          等待該線程終止。
 void join(long millis)
          等待該線程終止的時間最長爲 millis 毫秒。
 void join(long millis, int nanos)
          等待該線程終止的時間最長爲 millis 毫秒 + nanos 納秒。
       線程合併是優先執行調用該方法的線程,再執行當前線程,來看一個小例子:
[java] view plain copy
 print?在CODE上查看代碼片派生到我的代碼片
  1. public class JoinTest {  
  2.     public static void main(String[] args) throws InterruptedException {  
  3.         JoinThread t1 = new JoinThread("t1");  
  4.         JoinThread t2 = new JoinThread("t2");  
  5.         t1.start();  
  6.         t2.start();  
  7.         t1.join();  
  8.         t2.join();  
  9.         System.out.println("主線程開始執行!");  
  10.     }  
  11. }  
  12. class JoinThread extends Thread {  
  13.     public JoinThread(String name) {  
  14.         super(name);  
  15.     }  
  16.     public void run() {  
  17.         for(int i = 1; i <= 10; i++)  
  18.             System.out.println(getName() + getId() + "執行了" + i + "次");  
  19.     }  
  20. }  
       t1和t2都執行完才繼續主線程的執行,所謂合併,就是等待其它線程執行完,再執行當前線程,執行起來的效果就好像把其它線程合併到當前線程執行一樣。

線程優先級


       線程最低優先級爲1,最高優先級爲10,看起來就有10個級別,但這10個級別能不能和CPU對應上,還未可知,Thread類中提供了優先級的三個常量,如下:
java.lang.Thread
public static final int MAX_PRIORITY 10
public static final int MIN_PRIORITY 1
public static final int NORM_PRIORITY 5
       我們創建線程對象後,如果不顯示的設置優先級的話,默認爲5。優先級可以看成一種特權,優先級高的,獲取CPU調度的機會就大,優先級低的,獲取CPU調度的機會就小,這個和我們現實生活很一樣啊,優勝劣汰。線程優先級的示例就不寫了,比較簡單。

wait()和sleep()區別


       區別太大了,但是在Java線程面試題中是很常見的問題,相信你閱讀過本專欄後,能夠輕鬆的解答,這裏不再贅述。
       本文來自:高爽|Coder,原文地址:http://blog.csdn.net/ghsau/article/details/17560467,轉載請註明。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章