zoukankan      html  css  js  c++  java
  • (原创)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
                }
            }
    View Code

    s的预设值那里有改变:String s="aa";也没发现变为null,是没发生赋值。在异常中s也没有被赋空值。

    所以在使用get(long timeout, TimeUnit unit)的时候,变量初始最好能给一个空值,这样就不会产生奇怪的结果,这也是合理的编程习惯。

  • 相关阅读:
    让元素水平和垂直居中的方法总结
    a标签常见问题
    centos7的安装
    java面向对象-类的定义
    java内存划分
    java基础知识点-数组
    java基础知识点-重载
    第一章:服务器的组成知识点
    java 第二天运算符及录入函数Scanner
    java第一天 数据类型、变量的命名、类型的转换
  • 原文地址:https://www.cnblogs.com/starcrm/p/5010863.html
Copyright © 2011-2022 走看看