Java創建線程方法

本文總結自《瘋狂Java講義》

一、繼承Thread類

  1. 定義 Thread 類的子類,並重寫該類的 run() 方法,該 run() 方法的方法體就代表了線程需要完成的任務。因此把 run() 方法稱爲線程執行體。
  2. 創建 Thread 子類的實例,即創建了線程對象。
  3. 調用線程對象的 start() 方法來啓動該線程。

示例代碼如下:

public class ThreadTest extends Thread{
    private int x;
    public void run(){
        for (; x < 10; x++) {
            System.out.println(getName() + " " + x);    //獲取線程名字
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+ " " + i);    //獲取當前線程的名字
            if(i == 5){
                new ThreadTest().start();
                new ThreadTest().start();
            }
        }
    }
}

輸出結果:

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

我們可以看到,main函數是最先執行完的,因爲開闢一個線程後,該線程就會進入就緒狀態,並不會直接進入運行狀態,狀態的切換由底層平臺控制的,有一定的隨機性。

如果main函數的循環次數再大一些,線程0和線程1就能在main函數執行完前開始執行。

二、實現Runnable接口

  1. 定義 Runnable 接口的實現類,並重寫該接口的 run() 方法,該 run() 方法的方法體同樣是該線程的線程執行體。
  2. 創建 Runnable 實現類的實例,並以此實例作爲 Thread 的 target 來創建 Thread 對象,該 Thread 對象纔是真正的線程對象。
  3. 調用線程對象的 start() 方法來啓動線程

示例代碼如下:

public class ThreadTest implements Runnable{
    private int x;
    public void run(){
        for (; x < 10; x++) {
            System.out.println(Thread.currentThread().getName() + " " + x);
        }
    }
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName()+ " " + i);
            if(i == 0){
                new Thread(new ThreadTest()).start();
                new Thread(new ThreadTest()).start();
            }
        }
    }
}

輸出結果:

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

區別主要在於,線程的創建是將實現了 Runnable 的類的實例作爲參數,通過 new Thread(new threadTest())方法來創建,再調用線程的 start() 方法執行線程。

三、使用Callable和Future

  1. 創建 Callable 接口的實現類 ,並實現 call() 方法,該 call() 方法將作爲線程執行體,且該 call() 方法有返回值,再創建 Callable 實現類的實例。
  2. 使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該Callable 對象的 call() 方法的返回值。
  3. 使用 FutureTask 對象作爲 Thread 對象的 target 創建並啓動新線程。
  4. 調用 FutureTask 對象的 get() 方法來獲得子線程執行結束後的返回值。

示例代碼如下:

import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;
public class ThreadTest {
    public static void main(String[] args) {
        FutureTask<Integer> task = new FutureTask<Integer>(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int i = 0;
                for (; i < 10; i++){
                    System.out.println(Thread.currentThread().getName() + " 的循環變量i的值:" + i);
                }
                return i;
            }
        });
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + "的循環變量i的值:" + i);
            if (i == 5){
                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 接口的增強版,call() 作爲線程執行體,可以有返回值,還可以拋出異常。

Callable 接口沒有實現 Runnable 接口,不能作爲 Thread 的 target ,無法直接創建線程對象。

Callable 接口有泛型限制,返回值類型要與泛型類型一致。

Future 接口代表 Callable 接口裏 call() 方法的返回值,並提供了一個實現類 FutureTask ,該實現類也實現了 Runnable 接口,因此可以將 FutureTask 與 Callable 關聯起來,實現多線程。

三種方法的對比

  • 繼承 Thread 的方法已經繼承了 Thread 類,無法再繼承別的類,而 Runnable 和 Callable 可以繼承別的類。
  • Runnable 和 Callable 可以多個線程共享同一個 target 對象,所以非常適合多個相同線程來處理同一份資源的情況,從而可以將CPU、代碼、數據分開,形成清晰的模型,較好地體現了面向對象的思想。
  • Callable 類的線程可以經過 Future 包裝後獲取返回值。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章