• Future、Callable 、FutureTask详解


    1.Future和Callable


    Future是一个接口表示异步计算的结果,它提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。Future提供了get()、cancel()、isCancel()、isDone()四种方法,表示Future有三种功能:

    1、判断任务是否完成

    2、中断任务

    3、获取任务执行结果

     

    Callable和Runnable差不多,两者都是为那些其实例可能被另一个线程执行的类而设计的,最主要的差别在于Runnable不会返回线程运算结果,Callable可以(假如线程需要返回运行结果)

     1 public class CallableAndFuture {
     2     public static class CallableThread implements Callable<String> {
     3 
     4         @Override
     5         public String call() throws Exception {
     6             Thread.sleep(3000);
     7             System.out.println("方法A过了3秒钟才返回数据");
     8             return "A返回结果";
     9         }
    10 
    11     }
    12 
    13     public static void main(String[] args) throws InterruptedException, ExecutionException {
    14         ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
    15         CallableThread cThread = new CallableThread();
    16         Future<String> submit = newCachedThreadPool.submit(cThread);
    17         System.out.println(submit.get());
    18     }
    19 
    20 }

    输出:

    方法A过了3秒才返回结果

    2.FutureTask


    先上个FutureTask的类图

    FutureTask实现了Runnable和Future,实际上是这两个接口的包装器,所以FutureTask既是Runnable也是Future

    我们先写个基本的例子看看FutureTask的使用

    Callable<Integer> myComputation = ...;
    FutureTask<Integer> task = new FutureTask<Integer>(myComputation);
    Thread t = new Thread(task);
    t.start();
    ...
    Integer result = task.get(); //获取结果

    再看下他的构造方法

    相比第一个构造方法,第二个构造方法里面把Runnable转成了callable,所以两个构造方法实现的功能其实都差不多。

    FutureTask里面最重要的方法就是get方法了,该方法实际上是对Future的get的实现,下面我们研究下FutureTask的代码

    1.FutureTask的状态转换过程:

      private static final int NEW = 0; // 任务新建和执行中
      private static final int COMPLETING = 1; // 任务将要执行完毕
      private static final int NORMAL = 2; // 任务正常执行结束
      private static final int EXCEPTIONAL = 3; // 任务异常
      private static final int CANCELLED = 4; // 任务取消
      private static final int INTERRUPTING = 5; // 任务线程即将被中断
      private static final int INTERRUPTED = 6; // 任务线程已经中断




    * NEW -> COMPLETING -> NORMAL * NEW -> COMPLETING -> EXCEPTIONAL * NEW -> CANCELLED * NEW -> INTERRUPTING -> INTERRUPTED

    2.具体的get方法如下

    实现阻塞效果的是awaitDone,具体如下

     1 private int awaitDone(boolean timed, long nanos)
     2         throws InterruptedException {
     3             //先定义一堆变量
     4         final long deadline = timed ? System.nanoTime() + nanos : 0L;
     5         WaitNode q = null;
     6         boolean queued = false;
     7         //注意,这个是死循环,阻塞有他一半的功劳
     8         for (;;) {
     9             //刚开始线程还没加入到阻塞队列中这段代码是没有用的,
    10             //这里的作用是当线程已加入队列后,这时候线程被中断了,那就把线程从队列中移除
    11             if (Thread.interrupted()) {
    12                 removeWaiter(q);
    13                 throw new InterruptedException();
    14             }
    15 
    16             int s = state;
    17             //任务结束,返回状态
    18             if (s > COMPLETING) {
    19                 if (q != null)
    20                     q.thread = null;
    21                 return s;
    22             }
    23             //将要结束,那就再等等呗
    24             else if (s == COMPLETING) // cannot time out yet
    25                 Thread.yield();
    26             //初始化
    27             else if (q == null)
    28                 q = new WaitNode();
    29             //还没加入队列,那就用CAS加进去呗
    30             else if (!queued)
    31                 queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
    32                                                      q.next = waiters, q);
    33             //将线程挂起来,这里就阻塞了
    34             else if (timed) {
    35                 nanos = deadline - System.nanoTime();
    36                 if (nanos <= 0L) {
    37                     removeWaiter(q);
    38                     return state;
    39                 }
    40                 LockSupport.parkNanos(this, nanos);
    41             }
    42             else
    43                 LockSupport.park(this);
    44         }
    45     }

    上述讲述的是FutureTask的阻塞实现,其实还有个疑惑,当线程运行完毕,阻塞会自动解除获取结果,这究竟是怎么实现的呢

    我们看看线程的run方法

    这里有个ran变量,当获取到执行结果后ran变量为true,再执行set方法

    这个可以看出正常情况下FutureTask的状态变化是

    NEW -> COMPLETING -> NORMAL

    我们再看出 finishCompletion

    哈,找到了,类似于AQS的共享锁,这里也做了持续的唤醒

     

  • 相关阅读:
    MyGame--java语言编写的打飞机游戏(附源码下载)
    调用MyFocus库,简单实现二十几种轮播效果
    aBowman >>可以运用到自己博客上的小插件
    css通用小笔记03——浏览器窗口变小 div错位的问题
    css通用小笔记02——浮动、清除(三个例子)
    css通用小笔记01——导航背景
    PHP强制转换类型
    数据库---查询语句(三):高级查询
    数据库---T-SQL语句:查询语句(二)
    数据库---T-SQL语句(一)
  • 原文地址:https://www.cnblogs.com/xmzJava/p/7728703.html
Copyright © 2020-2023  润新知