與人有生老病死一樣,線程也同樣要經曆開始(等待)、運作、挂起和停止四種不同的狀态。這四種狀态都可以通過Thread類中的方法進行控制。下面給出了Thread類中和這四種狀态相關的方法。
1 // 開始線程
2 public void start( );
3 public void run( );
4
5 // 挂起和喚醒線程
6 public void resume( ); // 不建議使用
7 public void suspend( ); // 不建議使用
8 public static void sleep(long millis);
9 public static void sleep(long millis, int nanos);
10
11 // 終止線程
12 public void stop( ); // 不建議使用
13 public void interrupt( );
14
15 // 得到線程狀态
16 public boolean isAlive( );
17 public boolean isInterrupted( );
18 public static boolean interrupted( );
19
20 // join方法
21 public void join( ) throws InterruptedException;
複制
一、建立并運作線程
線程在建立後并不馬上執行run方法中的代碼,而是處于等待狀态。線程處于等待狀态時,可以通過Thread類的方法來設定線程不各種屬性,如線程的優先級(setPriority)、線程名(setName)和線程的類型(setDaemon)等。
當調用start方法後,線程開始執行run方法中的代碼。線程進入運作狀态。可以通過Thread類的isAlive方法來判斷線程是否處于運作狀态。當線程處于運作狀态時,isAlive傳回true,當isAlive傳回false時,可能線程處于等待狀态,也可能處于停止狀态。下面的代碼示範了線程的建立、運作和停止三個狀态之間的切換,并輸出了相應的isAlive傳回值。
1 package chapter2;
2
3 public class LifeCycle extends Thread
4 {
5 public void run()
6 {
7 int n = 0;
8 while ((++n) < 1000);
9 }
10
11 public static void main(String[] args) throws Exception
12 {
13 LifeCycle thread1 = new LifeCycle();
14 System.out.println("isAlive: " + thread1.isAlive());
15 thread1.start();
16 System.out.println("isAlive: " + thread1.isAlive());
17 thread1.join(); // 等線程thread1結束後再繼續執行
18 System.out.println("thread1已經結束!");
19 System.out.println("isAlive: " + thread1.isAlive());
20 }
21 }
複制
要注意一下,在上面的代碼中使用了join方法,這個方法的主要功能是保證線程的run方法完成後程式才繼續運作,這個方法将在後面的文章中介紹
上面代碼的運作結果:
isAlive: false
isAlive: true
thread1已經結束!
isAlive: false
複制
二、挂起和喚醒線程
一但線程開始執行run方法,就會一直到這個run方法執行完成這個線程才退出。但線上程執行的過程中,可以通過兩個方法使線程暫時停止執行。這兩個方法是suspend和sleep.在使用suspend挂起線程後,可以通過resume方法喚醒線程。而使用sleep使線程休眠後,隻能在設定的時間後使線程處于就緒狀态(線上程休眠結束後,線程不一定會馬上執行,隻是進入了就緒狀态,等待着系統進行排程)。
雖然suspend和resume可以很友善地使線程挂起和喚醒,但由于使用這兩個方法可能會造成一些不可預料的事情發生,是以,這兩個方法被辨別為deprecated(抗議)标記,這表明在以後的jdk版本中這兩個方法可能被删除,是以盡量不要使用這兩個方法來操作線程。下面的代碼示範了sleep、suspend和resume三個方法的使用。
1 package chapter2;
2
3 public class MyThread extends Thread
4 {
5 class SleepThread extends Thread
6 {
7 public void run()
8 {
9 try
10 {
11 sleep(2000);
12 }
13 catch (Exception e)
14 {
15 }
16 }
17 }
18 public void run()
19 {
20 while (true)
21 System.out.println(new java.util.Date().getTime());
22 }
23 public static void main(String[] args) throws Exception
24 {
25 MyThread thread = new MyThread();
26 SleepThread sleepThread = thread.new SleepThread();
27 sleepThread.start(); // 開始運作線程sleepThread
28 sleepThread.join(); // 使線程sleepThread延遲2秒
29 thread.start();
30 boolean flag = false;
31 while (true)
32 {
33 sleep(5000); // 使主線程延遲5秒
34 flag = !flag;
35 if (flag)
36 thread.suspend();
37 else
38 thread.resume();
39 }
40 }
41 }
複制
從表面上看,使用sleep和suspend所産生的效果類似,但sleep方法并不等同于suspend.它們之間最大的一個差別是可以在一個線程中通過suspend方法來挂起另外一個線程,如上面代碼中在主線程中挂起了thread線程。而sleep隻對目前正在執行的線程起作用。在上面代碼中分别使sleepThread和主線程休眠了2秒和5秒。在使用sleep時要注意,不能在一個線程中來休眠另一個線程。如main方法中使用thread.sleep(2000)方法是無法使thread線程休眠2秒的,而隻能使主線程休眠2秒。
在使用sleep方法時有兩點需要注意:
1. sleep方法有兩個重載形式,其中一個重載形式不僅可以設毫秒,而且還可以設納秒(1,000,000納秒等于1毫秒)。但大多數作業系統平台上的Java虛拟機都無法精确到納秒,是以,如果對sleep設定了納秒,Java虛拟機将取最接近這個值的毫秒。
2. 在使用sleep方法時必須使用throws或try{……}catch{……}.因為run方法無法使用throws,是以隻能使用try{……}catch{……}.當線上程休眠的過程中,使用interrupt方法(這個方法将在2.3.3中讨論)中斷線程時sleep會抛出一個InterruptedException異常。sleep方法的定義如下:
1 public static void sleep(long millis) throws InterruptedException
2 public static void sleep(long millis, int nanos) throws InterruptedException
複制
三、終止線程的三種方法
有三種方法可以使終止線程。
1. 使用退出标志,使線程正常退出,也就是當run方法完成後線程終止。
2. 使用stop方法強行終止線程(這個方法不推薦使用,因為stop和suspend、resume一樣,也可能發生不可預料的結果)。
3. 使用interrupt方法中斷線程。
1. 使用退出标志終止線程
當run方法執行完後,線程就會退出。但有時run方法是永遠不會結束的。如在服務端程式中使用線程進行監聽用戶端請求,或是其他的需要循環處理的任務。在這種情況下,一般是将這些任務放在一個循環中,如while循環。如果想讓循環永遠運作下去,可以使用while(true){……}來處理。但要想使while循環在某一特定條件下退出,最直接的方法就是設一個boolean類型的标志,并通過設定這個标志為true或false來控制while循環是否退出。下面給出了一個利用退出标志終止線程的例子。
1 package chapter2;
2
3 public class ThreadFlag extends Thread
4 {
5 public volatile boolean exit = false;
6
7 public void run()
8 {
9 while (!exit);
10 }
11 public static void main(String[] args) throws Exception
12 {
13 ThreadFlag thread = new ThreadFlag();
14 thread.start();
15 sleep(5000); // 主線程延遲5秒
16 thread.exit = true; // 終止線程thread
17 thread.join();
18 System.out.println("線程退出!");
19 }
20 }
複制
在上面代碼中定義了一個退出标志exit,當exit為true時,while循環退出,exit的預設值為false.在定義exit時,使用了一個Java關鍵字volatile,這個關鍵字的目的是使exit同步,也就是說在同一時刻隻能由一個線程來修改exit的值,
2. 使用stop方法終止線程
使用stop方法可以強行終止正在運作或挂起的線程。我們可以使用如下的代碼來終止線程:
1 thread.stop();
複制
雖然使用上面的代碼可以終止線程,但使用stop方法是很危險的,就象突然關閉計算機電源,而不是按正常程式關機一樣,可能會産生不可預料的結果,是以,并不推薦使用stop方法來終止線程。
3. 使用interrupt方法終止線程
使用interrupt方法來終端線程可分為兩種情況:
(1)線程處于阻塞狀态,如使用了sleep方法。
(2)使用while(!isInterrupted()){……}來判斷線程是否被中斷。
在第一種情況下使用interrupt方法,sleep方法将抛出一個InterruptedException例外,而在第二種情況下線程将直接退出。下面的代碼示範了在第一種情況下使用interrupt方法。
1 package chapter2;
2
3 public class ThreadInterrupt extends Thread
4 {
5 public void run()
6 {
7 try
8 {
9 sleep(50000); // 延遲50秒
10 }
11 catch (InterruptedException e)
12 {
13 System.out.println(e.getMessage());
14 }
15 }
16 public static void main(String[] args) throws Exception
17 {
18 Thread thread = new ThreadInterrupt();
19 thread.start();
20 System.out.println("在50秒之内按任意鍵中斷線程!");
21 System.in.read();
22 thread.interrupt();
23 thread.join();
24 System.out.println("線程已經退出!");
25 }
26 }
複制
上面代碼的運作結果如下:
在50秒之内按任意鍵中斷線程!
sleep interrupted
線程已經退出!
複制
在調用interrupt方法後, sleep方法抛出異常,然後輸出錯誤資訊:sleep interrupted.
注意:在Thread類中有兩個方法可以判斷線程是否通過interrupt方法被終止。一個是靜态的方法interrupted(),一個是非靜态的方法isInterrupted(),這兩個方法的差別是interrupted用來判斷目前線是否被中斷,而isInterrupted可以用來判斷其他線程是否被中斷。是以,while (!isInterrupted())也可以換成while (!Thread.interrupted())。
原文:http://java.chinaitlab.com/line/778850.html