Java線程Step By Step(Step 2)

(See http://www.suneca.com/article.asp?id=54

四、Join方法的使用    
Thread API 包含了等待另一個線程完成的方法:join() 方法。當調用 Thread.join() 時,調用線程將阻塞,直到被join方法加入的目標線程完成爲止。 可以於解起來抽象一睦,現在我們來舉一個例子說明問題。
 程序代碼
package zizz;

/**
* 使用繼承方式的線程實現.
* 
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public class MyThread extends Thread {
    
    public void run() {
        //循環輸出1 到 100
        for(int i=1;i<=10;i++){
            System.out.println("子線程輸出:" + i);
        }
    }
    
    public static void main(String[] args){
        //創建一個線程實例
        MyThread t1 = new MyThread();
        //線程進入運行態
        t1.start();
        //主線程輸出
        System.out.println("主線程輸出:http://www.suneca.com");
    }
}


輸出結果爲:


從結果我們可以看到,對於我們運行MyThread這個程序,在主線程當中創建一個新的子線程,新的子線程啓動後,主線程輸出博客地址。因爲當前主線程處於運行態,而子線程是處於可運行態,所以輸出的結果爲先輸出網址,再輸出了線程輸出的數據。但也有可能會出現這種情況,子線程進入可運行態之後,馬上進入運行態,那此時的是輸出子線程的數據,再輸出主線程的數據,它們的執行完全是由線程調度器進行調度。我們再將程序做如下的修改:
 程序代碼
package zizz;

/**
* 使用繼承方式的線程實現.
* 
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public class MyThread extends Thread {
    
    public void run() {
        //循環輸出1 到 100
        for(int i=1;i<=100;i++){
            System.out.println("子線程輸出:" + i);
        }
    }
    
    /**
     * 應用程序入口.
     * 
     * @param args
     */

    public static void main(String[] args){
        //創建一個線程實例
        MyThread t1 = new MyThread();
        //線程進入運行態
        t1.start();
        //主線程輸出
        for(int i=0;i<100;i++){
            System.out.println("主線程輸出:http://www.suneca.com");
        }
    }
}


此時的輸出結果爲:



主線程跟子線程在線程調度器的調度下,相互搶奪CPU資源,交叉運行着!

那麼,如果我希望程序啓用子線程,必須等待着子線程執行完畢之後,主線程才能繼續執行下去,那該怎麼辦?那此時,我們就必須使用到join這個方法啦!當使用Thread.join()時,調用線程將阻塞。因爲子線程是由主線程進行調用的,所以當子線程調用到join這個函數時,主線程將阻塞,必須等待子線程執行完畢之後,才能繼續執行。使用join的程序:
 程序代碼
package zizz;

/**
* 使用繼承方式的線程實現.
* 
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public class MyThread extends Thread {
    
    public void run() {
        //循環輸出1 到 100
        for(int i=1;i<=10;i++){
            System.out.println("子線程輸出:" + i);
        }
    }
    
    /**
     * 應用程序入口.
     * 
     * @param args
     */

    public static void main(String[] args){
        //創建一個線程實例
        MyThread t1 = new MyThread();
        //線程進入運行態
        t1.start();
        try {
            //線程的調用者,即主線程阻塞!
            t1.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //主線程輸出
        for(int i=0;i<10;i++){
            System.out.println("主線程輸出:http://www.suneca.com");
        }
    }
}


輸出結果爲:



從結果當中,我們可以看出,當子線程調用到join的時候,主線程將阻塞,子線程執行完畢之後,主線程才能繼續執行。對於join()方法,還有兩個重載方法,比如joing(long millis),那就是讓主程序阻塞多長時間後才能恢復到可運行狀態。

如果主程序調用了兩個子線程,那這兩個子線程是如何工作的呢?
 程序代碼
package zizz;

/**
* 使用繼承方式的線程實現.
* 
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public class MyThread extends Thread {
    
    public MyThread(String name){
        super(name);
    }
    
    public void run() {
        //循環輸出1 到 100
        for(int i=1;i<=100;i++){
            System.out.println("子線程輸出:" + this.getName() + " - " + i);
        }
    }
    
    /**
     * 應用程序入口.
     * 
     * @param args
     */

    public static void main(String[] args){
        //創建一個線程實例
        MyThread t1 = new MyThread("t1");
        MyThread t2 = new MyThread("t2");
        //線程進入運行態
        t1.start();
        t2.start();
        try {
            //線程的調用者,即主線程阻塞!
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //主線程輸出
        for(int i=0;i<10;i++){
            System.out.println("主線程輸出:http://www.suneca.com");
        }
    }
}


輸出結果:



從圖的輸出結果我們可以看出:對於在線程t1跟線程t2當中都調用了線程的join()方法,那麼,調用t1、t2的主線程將阻塞,等到t1、t2都執行完畢之後,再執行主線程。對於子線程t1跟t2,它們的一個先後順序,如何進行調度,完全是由線程調度器進行管理!

五、多線程
從前面的例子我們已經可以看出,對於有兩個以上的線程同時啓動,那這些線程的執行的先後順序我們是沒有辦法預知的,因爲對於它們的調用完全是由線程調度器進行調用!當子線程t1跟t2同時處於運行狀態,那誰先執行誰慢執行,由線程調度器決定。那我們程序能否進行控制呢?答案是可以的,我們可以通過設計優先級別來進行控制。每一個線程,默認的優先級別爲5,值越高,表示優先級別越高(最高爲10),值越低(最低爲1),表示優先級別越低!當線先級別相等的時候,那就只有排隊等待着線程調度器的調用。
 程序代碼
package zizz;

/**
* 使用繼承方式的線程實現.
* 
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public class MyThread extends Thread {
    
    public MyThread(String name){
        super(name);
    }
    
    public void run() {
        //循環輸出1 到 100
        for(int i=1;i<=10;i++){
            System.out.println("子線程輸出:" + this.getName() + " - " + i);
        }
    }
    
    /**
     * 應用程序入口.
     * 
     * @param args
     */

    public static void main(String[] args){
        //創建一個線程實例
        MyThread t1 = new MyThread("t1");
        MyThread t2 = new MyThread("t2");
        
        //線程進入運行態
        t1.start();
        t2.start();
        //主線程輸出  
        System.out.println("主線程輸出:http://www.suneca.com");
        
    }
}


t1比t2先啓動,進入可運行態,排隊等候線程的調度。執行結果:



如果設置了線程的優先級,如:
 程序代碼
package zizz;

/**
* 使用繼承方式的線程實現.
* 
* @author <a href="http://www.suneca.com">zizz</a>
*
* @Create-Time:2008 下午10:25:33
*/

public class MyThread extends Thread {
    
    public MyThread(String name){
        super(name);
    }
    
    public void run() {
        //循環輸出1 到 100
        for(int i=1;i<=10;i++){
            System.out.println("子線程輸出:" + this.getName() + " - " + i);
        }
    }
    
    /**
     * 應用程序入口.
     * 
     * @param args
     */

    public static void main(String[] args){
        //創建一個線程實例
        MyThread t1 = new MyThread("t1");
        MyThread t2 = new MyThread("t2");
        
        //設置t1的的優先級別爲最低
        t1.setPriority(Thread.MIN_PRIORITY);
        //設置t2的優先級別爲最高
        t2.setPriority(Thread.MAX_PRIORITY);        
        
        //線程進入運行態
        t1.start();
        t2.start();
        //主線程輸出  
        System.out.println("主線程輸出:http://www.suneca.com");
        
    }
}


執行結果:

從結果當中我們可以看到,最早的時候是由主線程執行,主線程佔用着CPU資源,接着,線程t1啓動,線程t1進入排隊,等候着線程調度器的調度;再接着,線程t2啓動,線程t2也進入排隊。
我們從結果當中更可以發現一點,對於高於5的線程優先級別,它更容易從當中正在執行的線程(主線程)當中搶奪CPU資源。

多線程的另外一種情況就是,多個線程共享同一個線程實例。
 程序代碼
package zizz;

/**
* 使用實現Runnable接口的線程.
* 
* @author <a href='http://www.suneca.com'>ZIZZ</a>
*
* @Create-Time:2008 下午10:48:31
*/

public class MyRunnable implements Runnable{

    public void run() {
        for(int i=0;i<100;i++){
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }
    }
    
    public static void main(String[] args){
        //構造一個Runnable的實例
        MyRunnable runnable = new MyRunnable();
        //創建新的線程
        Thread t1 = new Thread(runnable,"T1");
        Thread t2 = new Thread(runnable,"T2");
        
        //線程啓動.
        t1.start();
        t2.start();
    }

}


執行結果片斷:



當多線程共享同一個線程實例的時候,我們需要考慮一下線程的同步問題。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章