線程創建方式-繼承 Thread 類、實現 Runnable 接口、Callable接口

在 Java 中,有多種方式來實現多線程。下面通過實例來分別介紹繼承 Thread 類、實現 Runnable 接口、Callable接口來創建線程。

1、繼承 Thread 類

1.1類圖

先來看下Thread的類圖:

在這裏插入圖片描述
​ 從類圖中可以看出,Runnable接口是一個函數式接口,裏面只有一個抽象的run()方法,Thread 類本質上就是實現了 Runnable 接口的一個實例。

​ 啓動線程的唯一方法就是通過 調用Thread類的 start()方法。start()方法是一個 native 方法,它會啓動一個新線程,並執行 run()方法。

​ 這種方式實現多線程很簡單,通過自己的類直接 extend Thread,並複寫 run()方法,就可以啓動新線程並執行自己定義的 run()方法。

對於線程的啓動過程,會在下一章節進行詳細講解。

1.2 實例

public class ThreadDemo extends Thread {

    @Override
    public void run() {
        System.out.println("運行線程-->"+Thread.currentThread().getName());
    }

    public static void main(String[] args) {
        ThreadDemo threadDemo1 = new ThreadDemo();
        ThreadDemo threadDemo2 = new ThreadDemo();

        threadDemo1.start();
        threadDemo2.start();

    }
}

2、實現 Runnable 接口

如果自己的類已經 extends 另一個類,就無法直接 繼承Thread,此時,可以實現一個 Runnable 接口。定義runnable接口的實現類,並重寫該接口的run()方法。

實現方式如下:

public class RunnableDemo implements Runnable{
    @Override
    public void run() {
        System.out.println("運行線程-->"+Thread.currentThread().getName());
    }
    public static void main(String[] args) {
        Thread t1=new Thread(new RunnableDemo());
        t1.start();
        System.out.println("運行線程-->"+Thread.currentThread().getName());
    }
}

3、實現 Callable 接口

3.1 類圖

在這裏插入圖片描述

加入了Thread繼承關係的類圖:

在這裏插入圖片描述

通過類圖可以清楚的看出,FutureTask類實現了RunnableFuture接口,RunnableFuture又繼承了RunnableFuture兩個接口。

​ Callable對象不能直接作爲Thread對象的target,因爲Callable接口是 Java 5 新增的接口,不是Runnable接口的子接口。對於這個問題的解決方案,就引入 Future接口,此接口可以接受call() 的返回值,RunnableFuture接口是Future接口和Runnable接口的子接口,可以作爲Thread對象的target 。並且, Future 接口提供了一個實現類:FutureTask 。

FutureTask實現了RunnableFuture接口,可以作爲 Thread對象的target。

​ 實現 Callable 接口可以通過 Future Task 包裝器來創建 Thread 線程,有的時候,我們可能需要讓一步執行的線程在執行完成以後,提供一個返回值給到當前的主線程,主線程需要依賴這個值進行後續的邏輯處理,那麼這個時候,就需要用到帶返回值的線程了。

簡單理解一下就是:這是一個可以帶返回值的線程。

3.2實例

public class CallableDemo implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("運行線程-->"+Thread.currentThread().getName());
        return "SUCCESS";
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        CallableDemo2 callableDemo = new CallableDemo2();
        FutureTask<String> futureTask = new FutureTask<>(callableDemo2);
        new Thread(futureTask).start();
        Thread.sleep(100);
        System.out.println(futureTask.get());
    }
}

4、Runnable和Callable區別

通過上述三種方式,其實可以歸爲兩類:繼承類和實現接口兩種方式。相比繼承, 接口實現可以更加靈活,不會受限於Java的單繼承機制。並且通過實現接口的方式可以共享資源,適合多線程處理同一資源的情況。下面簡單介紹一下兩種接口類實現的區別:

  • 1)Callable規定的方法是call(),Runnable規定的方法是run();

  • 2)Callable的任務執行後可返回值,而Runnable的任務是不能返回值得;

  • 3)call方法可以拋出異常,run方法不可以,因爲run方法本身沒有拋出異常,所以自定義的線程類在重寫run的時候也無法拋出異常;

  • 4)運行Callable任務可以拿到一個Future對象,表示異步計算的結果。它提供了檢查計算是否完成的方法,以等待計算的完成,並檢索計算的結果。通過Future對象可以瞭解任務執行情況,可取消任務的執行,還可獲取執行結果。

5、start()和run()的區別

  • start()方法用來開啓線程,但是線程開啓後並沒有立即執行,他需要獲取cpu的執行權(分配的時間片)纔可以執行;
  • run()方法是由jvm創建完本地操作系統級線程後回調的方法,不可以手動調用(否則就是普通方法)。

總結:

本篇主要介紹了線程的三種創建方式,下一篇來介紹一下線程的啓動過程,一個線程到底是怎麼創建出來,又是怎麼啓動運行的呢,在下一篇你將會找到答案。

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