直接上代碼
import java.util.concurrent.Callable; public class MyCallable implements Callable<String> { private long waitTime; public MyCallable(int timeInMillis){ this.waitTime=timeInMillis; } @Override public String call() throws Exception { Thread.sleep(waitTime); return Thread.currentThread().getName(); } }
結果阻塞的代碼
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.FutureTask; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class FutureTaskExample { public static void main(String[] args) { MyCallable callable1 = new MyCallable(1000); MyCallable callable2 = new MyCallable(2000); FutureTask<String> futureTask1 = new FutureTask<String>(callable1); FutureTask<String> futureTask2 = new FutureTask<String>(callable2); ExecutorService executor = Executors.newFixedThreadPool(2); executor.execute(futureTask1); executor.execute(futureTask2); while (true) { try { if(futureTask1.isDone() && futureTask2.isDone()){ System.out.println("Done"); //shut down executor service executor.shutdown(); return; } if(!futureTask1.isDone()){ //阻塞futureTask1 System.out.println("FutureTask1 output="+futureTask1.get()); } if(!futureTask2.isDone()){ //阻塞futureTask2 System.out.println("FutureTask2 output="+futureTask2.get()); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }catch(Exception e){ //do nothing } } } }
運行結果很簡單,必須是:
FutureTask1 output=pool-1-thread-1
FutureTask2 output=pool-1-thread-2
Done
如果改為阻塞超時,先猜猜輸出結果是什么。注意第37行代碼有超時處理。
1 import java.util.concurrent.ExecutionException; 2 import java.util.concurrent.ExecutorService; 3 import java.util.concurrent.Executors; 4 import java.util.concurrent.FutureTask; 5 import java.util.concurrent.TimeUnit; 6 import java.util.concurrent.TimeoutException; 7 8 public class FutureTaskExample { 9 10 public static void main(String[] args) { 11 MyCallable callable1 = new MyCallable(1000); 12 MyCallable callable2 = new MyCallable(2000); 13 14 FutureTask<String> futureTask1 = new FutureTask<String>(callable1); 15 FutureTask<String> futureTask2 = new FutureTask<String>(callable2); 16 17 ExecutorService executor = Executors.newFixedThreadPool(2); 18 executor.execute(futureTask1); 19 executor.execute(futureTask2); 20 21 while (true) 22 { 23 try { 24 if(futureTask1.isDone() && futureTask2.isDone()){ 25 System.out.println("Done"); 26 //shut down executor service 27 executor.shutdown(); 28 return; 29 } 30 31 if(!futureTask1.isDone()){ 32 //阻塞futureTask1 33 System.out.println("FutureTask1 output="+futureTask1.get()); 34 } 35 36 System.out.println("Waiting for FutureTask2 to complete"); 37 String s = futureTask2.get(500L, TimeUnit.MILLISECONDS); //阻塞500毫秒 38 if(s !=null){ 39 System.out.println("FutureTask2 output="+s); 40 } 41 else{ 42 System.out.println("FutureTask2 output is null"); 43 } 44 } catch (InterruptedException | ExecutionException e) { 45 e.printStackTrace(); 46 }catch(Exception e){ 47 //do nothing 48 } 49 } 50 51 } 52 53 }
如果說是這樣的結果,那就錯了
FutureTask1 output=pool-1-thread-1
Waiting for FutureTask2 to complete
FutureTask2 output is null
Waiting for FutureTask2 to complete
FutureTask2 output is null
FutureTask2 output=pool-1-thread-2
Done
最終輸出
FutureTask1 output=pool-1-thread-1
Waiting for FutureTask2 to complete
Waiting for FutureTask2 to complete
FutureTask2 output=pool-1-thread-2
Done
說明了一件事,即在超時期限內,如果未能獲取線程返回值,futureTask2.get(500L, TimeUnit.MILLISECONDS) 將不對繼續執行后面的代碼,而是進行下一次的while操作了(並不是返回null),while的下一次循環,直到獲取到了返回結果,String s才得以賦值,代碼繼續進行。
所以要慎用get(long timeout, TimeUnit unit)。
傳統的理解是錯誤的:
get(long timeout, TimeUnit unit)用來獲取執行結果,如果在指定時間內,還沒獲取到結果,就直接返回null。
大神 海子 曾對這個問題有質疑,認為會拋出異常,並賦空值,見:
http://www.cnblogs.com/dolphin0520/p/3949310.html#3318489
我嘗試修改代碼

String s="aa"; while (true) { try { if(futureTask1.isDone() && futureTask2.isDone()){ System.out.println("Done"); //shut down executor service executor.shutdown(); return; } if(!futureTask1.isDone()){ //阻塞futureTask1 System.out.println("FutureTask1 output="+futureTask1.get()); } System.out.println("Waiting for FutureTask2 to complete"); s = futureTask2.get(500L, TimeUnit.MILLISECONDS); //阻塞500毫秒 if(s !=null){ System.out.println("FutureTask2 output="+s); } else{ System.out.println("FutureTask2 output is null"); } } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); }catch(Exception e){ System.out.println("s is:"+s); //do nothing } }
s的預設值那里有改變:String s="aa";也沒發現變為null,是沒發生賦值。在異常中s也沒有被賦空值。
所以在使用get(long timeout, TimeUnit unit)的時候,變量初始最好能給一個空值,這樣就不會產生奇怪的結果,這也是合理的編程習慣。