- Callable 介面只定義了一個 call() method,實作這個介面的好處是當執行緒執行結束後,可以傳回值,且值的型別可以由我們自定。
- FutureTask 並非 Future 唯一實作的類別,之後還會介紹別的實作 Future 介面的類別,在這篇我先介紹 FutureTask。
1 package idv.steven.concurrency; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.concurrent.Callable; 6 import java.util.concurrent.ExecutionException; 7 import java.util.concurrent.FutureTask; 8 9 public class FibonacciDemo implements Callable<List<Long>> { 10 private long number; 11 12 public FibonacciDemo(long number) { 13 this.number = number; 14 } 15 16 @Override 17 public List<Long> call() throws Exception { 18 List<Long> fib = new ArrayList<Long>(); 19 fib.add(0L); 20 fib.add(1L); 21 for(int i=2; i<number; i++) { 22 Long f = fib.get(fib.size()-1) + fib.get(fib.size()-2); 23 fib.add(f); 24 } 25 26 return fib; 27 } 28 29 public static void main(String[] args) { 30 Callable<List<Long>> fibonacci = new FibonacciDemo(10); 31 FutureTask<List<Long>> fibonacciTask = new FutureTask<List<Long>>(fibonacci); 32 33 Thread t = new Thread(fibonacciTask); 34 t.start(); 35 36 try { 37 t.sleep(10); 38 39 boolean canceled = fibonacciTask.cancel(false); 40 System.out.println("canceled = " + canceled); 41 42 if (!fibonacciTask.isCancelled()) { 43 List<Long> fib = fibonacciTask.get(); 44 for(Long f:fib) { 45 System.out.print(f + " "); 46 } 47 } 48 49 // if (fibonacciTask.isDone()) { 50 // List<Long> fib = fibonacciTask.get(); 51 // for(Long f:fib) { 52 // System.out.print(f + " "); 53 // } 54 // } 55 // else { 56 // System.out.println("unfinished"); 57 // } 58 } 59 catch (InterruptedException | ExecutionException e) { 60 e.printStackTrace(); 61 } 62 } 63 }
程式說明如下:
- FutureTask 有兩個建構式,一個接受實作 Callable 的類別,另一個接受實作 Runnable 的類別,這個程式實作了 Callable (line 9),並指定傳回的值為 List<Long>,也就是所有計算所得的數字。大多數的人會選擇實作 Callable,因為實作 Runnable 的話,還要傳入一個變數,用來儲存傳回值。
- FutureTask 類別是一個實作 Runnable 介面的類別,要建立執行緒,仍需透過 Thread 類別,所以可以看到第 31 行傳入我們實作 Callable 的類別物件給 FutureTask 後,為了建立一個執行緒,第 33 行再將 FutureTask 類別的物件傳給 Thread,然後在第 34 行啟動一個新的執行緒。
- 第 17~27 行實作 call() method,計算費式數列後,傳回給主程式。
- 第 39~47 行及第 49~57 行是不同兩個版本,執行出來的結果是一樣的。
- 要如何取得 call() method 傳回的值呢? 使用 FutureTask 的 get() method ! 就算是實作 Runnable 介面,也是透過 get() 取得傳回值。(line 43、50)
- 先說明第一個版本,第 39 行是什麼意思呢? cancel() method 是試著強制中斷 FutureTask 的執行緒! 傳入的參數 true 表示,不管這個執行緒處於什麼狀態,都將它中斷結束,如果傳入的是 false,則是當執行緒已經進入 call() method 且還沒執行完離開 call() method 則不要中斷,否則都中斷。至於傳回值即是 true 表示程式沒有執行完就被中斷,false 則是有執行完。這也是為什麼會有第 37 行睡了 10 毫秒的原因,如果主程式不略微停頓一下,讓 FutureTask 的那個執行緒計算一下費式數列,第 39 行有可能傳回 true,也就是程式被中斷了!
- 第 42 行判斷是否有被中斷,如果沒有,就是有順利的計算完傳回值,所以第 43 行呼叫 get() 取得傳回值,接下來的 for 迴圈當然就是印出結果。
- 再來說明第二個版本,同樣的要保留第 37 行,讓費式數列有時間被計算,第 49 行和第 42 行剛好相反,它會判斷執行緒是否有被執行完? 有的話就傳回 true,沒有就傳回 false。傳回 true 的話,第 50 行取得計算結果然後印出來。
沒有留言:
張貼留言