直接上代码
import java.util.concurrent.Callable; public class MyCallable implements Callable{ 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); FutureTaskfutureTask1 = new FutureTask (callable1); FutureTask futureTask2 = new FutureTask (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-2Done
如果改为阻塞超时,先猜猜输出结果是什么。注意第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 FutureTaskfutureTask1 = new FutureTask (callable1);15 FutureTask futureTask2 = new FutureTask (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 service27 executor.shutdown();28 return;29 }30 31 if(!futureTask1.isDone()){32 //阻塞futureTask133 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 nothing48 }49 }50 51 }52 53 }
如果说是这样的结果,那就错了
FutureTask1 output=pool-1-thread-1
Waiting for FutureTask2 to completeFutureTask2 output is nullWaiting for FutureTask2 to completeFutureTask2 output is nullFutureTask2 output=pool-1-thread-2Done
最终输出
FutureTask1 output=pool-1-thread-1
Waiting for FutureTask2 to completeWaiting for FutureTask2 to completeFutureTask2 output=pool-1-thread-2Done
说明了一件事,即在超时期限内,如果未能获取线程返回值,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)的时候,变量初始最好能给一个空值,这样就不会产生奇怪的结果,这也是合理的编程习惯。