Java線程的學習_線程的創建和啓動

線程的創建和啓動

Java使用Thread類代表線程,所有線程對象都必須是Thread類或其子類的實例。每個線程的作用是完成一定的任務,實際上就是執行一段程序流(一段順序執行的的代碼)。Java使用線程執行體來代表這段流程。

創建線程有三個方法:
- 方法1.繼承Thread類創建線程類
- 方法2.實現Runnable接口創建線程類
- 方法3.使用Callable和Future創建線程


方法1

package com.thread.create_and_start;

/**
 * 繼承Thread類創建多線程
 * 創建並啓動多線程步驟:
 * 定義Thread類的子類,並重寫該類的run()方法,該run()方法的方法體就代表了線程需要完成的任務。因此把run()方法稱爲線程執行體
 * 創建Thread子類的實例,即創建了線程對象
 * 調用線程對象的start()方法來啓動該線程
 * @author tengyu
 *
 */
public class FirstThread extends Thread{

    private int i;
    //重寫run()方法,run()方法的方法體就是線程執行體
    public void run(){

        for(; i < 10; i++){
            //當線程類繼承Thread類時,直接使用this即可獲取當前線程
            //Thread對象的getName()返回當前線程的名字
            //因此可以直接調用getName()方法返回當前線程的名字
            System.out.println(getName()  + " " + i);
        }
    }

    public static void main(String[] args){

        for(int i = 0; i < 10; i++){
            //調用Thread的currentThread()方法獲取當前線程
            System.out.println(Thread.currentThread().getName() + " " + i);
            if(i == 2){
                //創建並啓動第一個線程
                new FirstThread().start();
                //創建並啓動第二個線程
                new FirstThread().start();
            }
        }
    }
}

得到結果:

main 0
main 1
main 2
Thread-0 0
Thread-0 1
main 3
Thread-0 2
Thread-1 0
main 4
main 5
main 6
Thread-1 1
Thread-0 3
Thread-1 2
main 7
Thread-1 3
Thread-0 4
Thread-0 5
Thread-0 6
Thread-0 7
Thread-0 8
Thread-0 9
Thread-1 4
Thread-1 5
main 8
main 9
Thread-1 6
Thread-1 7
Thread-1 8
Thread-1 9

當循環變量i=2時,創建並啓動了兩個新線程。並且兩個線程的循環變量不連續表明它們沒有共享數據。
上面程序用到的方法:
Thread.currentThread():currentThread()是Thread類的靜態方法,該 方法總是返回當前正在執行的線程對象。
getName():該方法是Thread類的實例方法,該方法返回調用該方法的線程名字。

方法2

package com.thread.create_and_start;

/**
 * Runnable接口創建線程類
 * 步驟:
 * 定義Runnable接口的實現類,並重寫該接口的run()方法,該run()方法的方法體同樣是該線程的線程執行體
 * 創建Runnable實現類的實例,並以此實例作爲Thread的target來創建Thread對象,該Thread對象纔是真正的線程對象
 * 調用線程對象的start()方法來啓動線程
 * @author tengyu
 *
 */
//通過實現Runnable接口來創建線程類
public class SecondThread implements Runnable{

    private int i;
    public static void main(String[] args) {
        // TODO 自動生成的方法存根
        for(int i = 0; i < 10; i++){
            System.out.println(Thread.currentThread().getName() + " " + i);
            if( i == 2){
                SecondThread st = new SecondThread();
                //通過new Thread(target , name)方法創建新線程
                new Thread(st , "新線程1").start();
                new Thread(st , "新線程2").start();
            }
        }
    }

    @Override
    //接口需要實現的方法,線程執行體
    public void run() {
        // TODO 自動生成的方法存根
        for( ; i < 10; i++){
            //當線程類實現Runnable接口時
            //如果想獲取當前線程,只能用Thread.currentThread().getName()
            System.out.println(Thread.currentThread().getName() + " " + i);
        }
    }

}

運行程序:

main 0
main 1
main 2
新線程1 0
新線程1 1
新線程1 2
新線程1 3
新線程1 4
新線程1 5
新線程1 6
main 3
新線程2 7
新線程1 7
新線程2 8
main 4
新線程1 9
main 5
main 6
main 7
main 8
main 9

線程1,2的i變量是連續的,說明採用該方法創建的多個線程可以共享線程類的實例變量。

方法3

package com.thread.create_and_start;

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

/**
 * 使用Callable和Future創建線程
 * 創建並啓動有返回值的線程的步驟
 * 創建Callable接口的實現類,並實現call()方法,該call()方法將作爲線程執行體,且該call()方法有返回值,再創建
 *  Callable實現類的實例
 * 使用FutureTask類來包裝Callable對象,該FutureTask對象封裝了該Callable對象的call()方法的返回值
 * 使用FutureTask對象作爲Thread對象的target創建並啓動線程
 * 調用FutureTask對象的get()方法來獲得子線程執行結束後的返回值
 * @author tengyu
 *
 */
public class ThirdThread {

    public static void main(String[] args){
        //創建Callable對象
        ThirdThread rt = new ThirdThread();
        //先使用Lambda表達式創建Callable<Integer>對象
        //使用FutureTask來包裝Callable對象
        FutureTask<Integer> task = new FutureTask<Integer>((Callable<Integer>)()->{
            int i = 0;
            for(; i < 10; i++){
                System.out.println(Thread.currentThread().getName() + " 的循環變量i的值:"
                        + i);
            }
            //call()方法可以有返回值
            return i;
        });
        for(int i = 0; i < 10; i++){
            System.out.println(Thread.currentThread().getName() + " 的循環變量i的值:"
                    + i);
            if(i == 2){
                //實質是以Callable對象來創建並啓動線程
                new Thread(task, "有返回值的線程").start();
            }
        }
        try{
            //獲取線程返回值
            System.out.println("線程返回值:" + task.get());
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

運行結果:

main 的循環變量i的值:0
main 的循環變量i的值:1
main 的循環變量i的值:2
main 的循環變量i的值:3
main 的循環變量i的值:4
main 的循環變量i的值:5
main 的循環變量i的值:6
main 的循環變量i的值:7
main 的循環變量i的值:8
main 的循環變量i的值:9
有返回值的線程 的循環變量i的值:0
有返回值的線程 的循環變量i的值:1
有返回值的線程 的循環變量i的值:2
有返回值的線程 的循環變量i的值:3
有返回值的線程 的循環變量i的值:4
有返回值的線程 的循環變量i的值:5
有返回值的線程 的循環變量i的值:6
有返回值的線程 的循環變量i的值:7
有返回值的線程 的循環變量i的值:8
有返回值的線程 的循環變量i的值:9
線程返回值:10

Callable接口可以看做是Runnable接口的加強版,Callable接口提供了一個call()方法可以作爲線程執行體,但是call()方法比run()方法更強大。強大之處在於call()方法可以有返回值,並且可以拋出異常。
call()方法並不能直接調用,所以使用Future接口來代表返回值。

三種方法的優缺點

通過繼承Thread類或實現Runnable、Callable接口都可以實現多線程,不過實現Runnable接口與實現Callable接口的方式基本相同,只是Callable接口裏定義的方法有返回值,可以聲明拋出異常而已。因此把Runnable接口和Callable接口歸爲一種方式。
採用實現Runnable、Callable接口的方式創建多線的優缺點:
-線程類只是實現了Runnable接口或Callable接口,還可以繼承其他類。
-在這種方式下,多個線程可以共享同一個target對象,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU、代碼和數據分開,形成清晰的模型,較好地體現了面向對象的思想。
-劣勢是編程稍稍複雜,如果需要訪問當前線程,則必須使用Thread.currentThread()方法。
採用繼承Thread類的方式創建多線程的優缺點:
-劣勢,不能繼承其它父類,因爲已經繼承了Thread類。
-優勢,編寫簡單,訪問當前線程直接使用this即可。

總體來講,一般採用Runnable接口和Callable接口的方式來創建多線程。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章