【原創】Java多執行緒初學者指南(4):執行緒的生命週期

銀河使者發表於2009-03-12

本文為原創,如需轉載,請註明作者和出處,謝謝!

    與人有生老病死一樣,執行緒也同樣要經歷開始(等待)、執行、掛起和停止四種不同的狀態。這四種狀態都可以通過Thread類中的方法進行控制。下面給出了Thread類中和這四種狀態相關的方法。

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt    // 開始執行緒
    public void start( );
    public void run( );

    
// 掛起和喚醒執行緒
    public void resume( );     // 不建議使用
    public void suspend( );    // 不建議使用
    public static void sleep(long millis);
    public static void sleep(long millis, int nanos);

    // 終止執行緒
    public void stop( );       // 不建議使用
    public void interrupt( );

    // 得到執行緒狀態
    public boolean isAlive( );
    
public boolean isInterrupted( );
    public static boolean interrupted( );

    // join方法
    public void join( ) throws InterruptedException;

一、建立並執行執行緒

執行緒在建立後並不馬上執行run方法中的程式碼,而是處於等待狀態。執行緒處於等待狀態時,可以通過Thread類的方法來設定執行緒不各種屬性,如執行緒的優先順序(setPriority)、執行緒名(setName)和執行緒的型別(setDaemon)等。

當呼叫start方法後,執行緒開始執行run方法中的程式碼。執行緒進入執行狀態。可以通過Thread類的isAlive方法來判斷執行緒是否處於執行狀態。當執行緒處於執行狀態時,isAlive返回true,當isAlive返回false時,可能執行緒處於等待狀態,也可能處於停止狀態。下面的程式碼演示了執行緒的建立、執行和停止三個狀態之間的切換,並輸出了相應的isAlive返回值。

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtpackage chapter2;

public class LifeCycle extends Thread
{
    
public void run()
    {
        
int n = 0;
        
while ((++n) < 1000);        
    }
     
    
public static void main(String[] args) throws Exception
    {
        LifeCycle thread1 
= new LifeCycle();
        System.out.println(
"isAlive: " + thread1.isAlive());
        thread1.start();
        System.out.println(
"isAlive: " + thread1.isAlive());
        thread1.join();  
// 等執行緒thread1結束後再繼續執行 
        System.out.println("thread1已經結束!");
        System.out.println(
"isAlive: " + thread1.isAlive());
    }
}

要注意一下,在上面的程式碼中使用了join方法,這個方法的主要功能是保證執行緒的run方法完成後程式才繼續執行,這個方法將在後面的文章中介紹
    上面程式碼的執行結果:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtisAlive: false
isAlive: true
thread1已經結束!
isAlive: false

二、掛起和喚醒執行緒

一但執行緒開始執行run方法,就會一直到這個run方法執行完成這個執行緒才退出。但線上程執行的過程中,可以通過兩個方法使執行緒暫時停止執行。這兩個方法是suspendsleep。在使用suspend掛起執行緒後,可以通過resume方法喚醒執行緒。而使用sleep使執行緒休眠後,只能在設定的時間後使執行緒處於就緒狀態(線上程休眠結束後,執行緒不一定會馬上執行,只是進入了就緒狀態,等待著系統進行排程)。

雖然suspendresume可以很方便地使執行緒掛起和喚醒,但由於使用這兩個方法可能會造成一些不可預料的事情發生,因此,這兩個方法被標識為deprecated(抗議)標記,這表明在以後的jdk版本中這兩個方法可能被刪除,所以儘量不要使用這兩個方法來操作執行緒。下面的程式碼演示了sleepsuspendresume三個方法的使用。

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtpackage chapter2;

public class MyThread extends Thread
{
    
class SleepThread extends Thread
    {
        
public void run()
        {
            
try
            {
                sleep(
2000);
            }
            
catch (Exception e)
            {
            }
        }
    }
    
public void run()
    {
        
while (true)
            System.out.println(
new java.util.Date().getTime());
    }
    
public static void main(String[] args) throws Exception
    {
        MyThread thread 
= new MyThread();
        SleepThread sleepThread 
= thread.new SleepThread();
        sleepThread.start(); 
// 開始執行執行緒sleepThread
        sleepThread.join();  // 使執行緒sleepThread延遲2秒
        thread.start();
        
boolean flag = false;
        
while (true)
        {
            sleep(
5000);  // 使主執行緒延遲5秒
            flag = !flag;
            
if (flag)
                thread.suspend(); 
            
else
                thread.resume();
        }
    }
}

從表面上看,使用sleepsuspend所產生的效果類似,但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方法時必須使用throwstry{...}catch{...}。因為run方法無法使用throws,所以只能使用try{...}catch{...}。當線上程休眠的過程中,使用interrupt方法(這個方法將在2.3.3中討論)中斷執行緒時sleep會丟擲一個InterruptedException異常。sleep方法的定義如下:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtpublic static void sleep(long millis)  throws InterruptedException
public static void sleep(long millis,  int nanos)  throws InterruptedException

三、終止執行緒的三種方法

有三種方法可以使終止執行緒。

1.  使用退出標誌,使執行緒正常退出,也就是當run方法完成後執行緒終止。

2.  使用stop方法強行終止執行緒(這個方法不推薦使用,因為stopsuspendresume一樣,也可能發生不可預料的結果)。

    3.  使用interrupt方法中斷執行緒。 

1. 使用退出標誌終止執行緒

run方法執行完後,執行緒就會退出。但有時run方法是永遠不會結束的。如在服務端程式中使用執行緒進行監聽客戶端請求,或是其他的需要迴圈處理的任務。在這種情況下,一般是將這些任務放在一個迴圈中,如while迴圈。如果想讓迴圈永遠執行下去,可以使用while(true){...}來處理。但要想使while迴圈在某一特定條件下退出,最直接的方法就是設一個boolean型別的標誌,並通過設定這個標誌為truefalse來控制while迴圈是否退出。下面給出了一個利用退出標誌終止執行緒的例子。

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtpackage chapter2;

public class ThreadFlag extends Thread
{
    
public volatile boolean exit = false;

    
public void run()
    {
        
while (!exit);
    }
    
public static void main(String[] args) throws Exception
    {
        ThreadFlag thread 
= new ThreadFlag();
        thread.start();
        sleep(
5000); // 主執行緒延遲5秒
        thread.exit = true;  // 終止執行緒thread
        thread.join();
        System.out.println(
"執行緒退出!");
    }
}

    在上面程式碼中定義了一個退出標誌exit,當exittrue時,while迴圈退出,exit的預設值為false。在定義exit時,使用了一個Java關鍵字volatile,這個關鍵字的目的是使exit同步,也就是說在同一時刻只能由一個執行緒來修改exit的值,

2. 使用stop方法終止執行緒

使用stop方法可以強行終止正在執行或掛起的執行緒。我們可以使用如下的程式碼來終止執行緒:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtthread.stop();

雖然使用上面的程式碼可以終止執行緒,但使用stop方法是很危險的,就象突然關閉計算機電源,而不是按正常程式關機一樣,可能會產生不可預料的結果,因此,並不推薦使用stop方法來終止執行緒。

3. 使用interrupt方法終止執行緒

使用interrupt方法來終端執行緒可分為兩種情況:

(1)執行緒處於阻塞狀態,如使用了sleep方法。

(2)使用while(!isInterrupted()){...}來判斷執行緒是否被中斷。

在第一種情況下使用interrupt方法,sleep方法將丟擲一個InterruptedException例外,而在第二種情況下執行緒將直接退出。下面的程式碼演示了在第一種情況下使用interrupt方法。

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gtpackage chapter2;

public class ThreadInterrupt extends Thread
{
    
public void run()
    {
        
try
        {
            sleep(
50000);  // 延遲50秒
        }
        
catch (InterruptedException e)
        {
            System.out.println(e.getMessage());
        }
    }
    
public static void main(String[] args) throws Exception
    {
        Thread thread 
= new ThreadInterrupt();
        thread.start();
        System.out.println(
"在50秒之內按任意鍵中斷執行緒!");
        System.in.read();
        thread.interrupt();
        thread.join();
        System.out.println(
"執行緒已經退出!");
    }
}

上面程式碼的執行結果如下:

<!--

Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/

--&gt    在50秒之內按任意鍵中斷執行緒!

    sleep interrupted
    執行緒已經退出!

    在呼叫interrupt方法後, sleep方法丟擲異常,然後輸出錯誤資訊:sleep interrupted

注意:在Thread類中有兩個方法可以判斷執行緒是否通過interrupt方法被終止。一個是靜態的方法interrupted(),一個是非靜態的方法isInterrupted(),這兩個方法的區別是interrupted用來判斷當前線是否被中斷,而isInterrupted可以用來判斷其他執行緒是否被中斷。因此,while (!isInterrupted())也可以換成while (!Thread.interrupted())

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12921506/viewspace-567096/,如需轉載,請註明出處,否則將追究法律責任。

相關文章