1. 程式人生 > 其它 >JUC學習筆記(五)

JUC學習筆記(五)

建立執行緒的方法-一種是通過建立 Thread 類,另一種是通過使用 Runnable 建立執行緒。但是,Runnable 缺少的一項功能是,當執行緒終止時(即 run()完成時),我們無法使執行緒返回結果。為了支援此功能,Java 中提供了 Callable 介面。

JUC學習筆記(一)https://www.cnblogs.com/lm66/p/15118407.html
JUC學習筆記(二)https://www.cnblogs.com/lm66/p/15118813.html
JUC學習筆記(三)https://www.cnblogs.com/lm66/p/15118976.html
JUC學習筆記(四)https://www.cnblogs.com/lm66/p/15122281.html

1、Callable&Future 介面

1.1、Callable介面

  建立執行緒的方法-一種是通過建立 Thread 類,另一種是通過使用 Runnable 建立執行緒。但是,Runnable 缺少的一項功能是,當執行緒終止時(即 run()完成時),我們無法使執行緒返回結果。為了支援此功能,Java 中提供了 Callable 介面。
建立執行緒的第三種方案---Callable 介面


Callable 介面的特點如下

  • 為了實現 Runnable,需要實現不返回任何內容的 run()方法,而對於Callable,需要實現在完成時返回結果的 call()方法。
  • call()方法可以引發異常,而 run()則不能。
  • 為實現 Callable 而必須重寫 call 方法
  • 不能直接替換 runnable,因為 Thread 類的構造方法根本沒有 Callable
class MyThread implements Runnable {
    @Override
    public void run() {
    }
}

class MyThread2 implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        return 200;
    }
}

1.2、Future介面

  當 call()方法完成時,結果必須儲存在主執行緒已知的物件中,以便主執行緒可以知道該執行緒返回的結果。為此,可以使用 Future 物件。將 Future 視為儲存結果的物件–它可能暫時不儲存結果,但將來會儲存(一旦Callable 返回)。Future 基本上是主執行緒可以跟蹤進度以及其他執行緒的結果的一種方式。要實現此介面,必須重寫 5 種方法,這裡列出了重要的方法,如下:

public boolean cancel(boolean mayInterrupt):用於停止任務。
如果尚未啟動,它將停止任務。如果已啟動,則僅在 mayInterrupt 為 true時才會中斷任務。


public Object get()丟擲 InterruptedException,ExecutionException:用於獲取任務的結果。
如果任務完成,它將立即返回結果,否則將等待任務完成,然後返回結果。
public boolean isDone():如果任務完成,則返回 true,否則返回 false
可以看到 Callable 和 Future 做兩件事-Callable 與 Runnable 類似,因為它封裝了要在另一個執行緒上執行的任務,而 Future 用於儲存從另一個執行緒獲得的結果。實際上,future 也可以與 Runnable 一起使用。
要建立執行緒,需要 Runnable。為了獲得結果,需要 future

1.3、FutrueTask

  Java 庫具有具體的 FutureTask 型別,該型別實現 Runnable 和 Future,並方便地將兩種功能組合在一起。 可以通過為其建構函式提供 Callable 來建立FutureTask。然後,將 FutureTask 物件提供給 Thread 的建構函式以建立Thread 物件。因此,間接地使用Callable 建立執行緒。
核心原理:(重點)
在主執行緒中需要執行比較耗時的操作時,但又不想阻塞主執行緒時,可以把這些作業交給 Future 物件在後臺完成

  • 當主執行緒將來需要時,就可以通過 Future 物件獲得後臺作業的計算結果或者執行狀態
  • 一般 FutureTask 多用於耗時的計算,主執行緒可以在完成自己的任務後,再去獲取結果。
  • 僅在計算完成時才能檢索結果;如果計算尚未完成,則阻塞 get 方法 一旦計算完成,就不能再重新開始或取消計算 get 方法而獲取結果只有在計算完成時獲取,否則會一直阻塞直到任務轉入完成狀態,然後會返回結果或者丟擲異常
  • get 只計算一次,因此 get 方法放到最後

1.4、使用Callable和Futrue

CallableDemo 案例

public class CallableDemo {

    public static void main(String[] args) throws Exception {
        Callable<String> callable = () -> {
            System.out.println("come in call");
            TimeUnit.SECONDS.sleep(3);
            return "return val";
        };
        FutureTask<String> futureTask = new FutureTask<>(callable);
        new Thread(futureTask).start();
        System.out.println("continue");
        String s = futureTask.get();
        System.out.println(s);
        System.out.println("over");
    }
}

1.5、小結

  • 在主執行緒中需要執行比較耗時的操作時,但又不想阻塞主執行緒時,可以把這些作業交給 Future 物件在後臺完成, 當主執行緒將來需要時,就可以通過 Future物件獲得後臺作業的計算結果或者執行狀態
  • 一般 FutureTask 多用於耗時的計算,主執行緒可以在完成自己的任務後,再去獲取結果
  • 僅在計算完成時才能檢索結果;如果計算尚未完成,則阻塞 get 方法。一旦計算完成,就不能再重新開始或取消計算。get 方法而獲取結果只有在計算完成時獲取,否則會一直阻塞直到任務轉入完成狀態,然後會返回結果或者丟擲異常。
  • 只計算一次