本文主要是介绍(原创)Callable、FutureTask中阻塞超时返回的坑点,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
直接上代码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)的时候,变量初始最好能给一个空值,这样就不会产生奇怪的结果,这也是合理的编程习惯。
这篇关于(原创)Callable、FutureTask中阻塞超时返回的坑点的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!