zoukankan      html  css  js  c++  java
  • Java多线程系列--“JUC线程池”06之 Callable和Future

    转自:http://www.cnblogs.com/skywang12345/p/3544116.html

    概要

    本章介绍线程池中的Callable和Future。

    • Callable 和 Future 简介
    • 示例和源码分析(基于JDK1.7.0_40)

    转载请注明出处:http://www.cnblogs.com/skywang12345/p/3544116.html

    Callable 和 Future 简介

      Callable 和 Future 是比较有趣的一对组合。当我们需要获取线程的执行结果时,就需要用到它们。Callable用于产生结果,Future用于获取结果。

    1. Callable

    Callable 是一个接口,它只包含一个call()方法。Callable是一个返回结果并且可能抛出异常的任务。

    为了便于理解,我们可以将Callable比作一个Runnable接口,而Callable的call()方法则类似于Runnable的run()方法。

    Callable的源码如下:

    public interface Callable<V> {
        V call() throws Exception;
    }

    说明:从中我们可以看出Callable支持泛型。

    2. Future

    Future 是一个接口。它用于表示异步计算的结果。提供了检查计算是否完成的方法,以等待计算的完成,并获取计算的结果。

    Future的源码如下:

    复制代码
    public interface Future<V> {
        // 试图取消对此任务的执行。
        boolean     cancel(boolean mayInterruptIfRunning)
    
        // 如果在任务正常完成前将其取消,则返回 true。
        boolean     isCancelled()
    
        // 如果任务已完成,则返回 true。
        boolean     isDone()
    
        // 如有必要,等待计算完成,然后获取其结果。
        V           get() throws InterruptedException, ExecutionException;
    
        // 如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
        V             get(long timeout, TimeUnit unit)
              throws InterruptedException, ExecutionException, TimeoutException;
    }
    复制代码

    说明Future用于表示异步计算的结果。它的实现类是FutureTask,在讲解FutureTask之前,我们先看看Callable, Future, FutureTask它们之间的关系图,如下:

    说明
    (01) RunnableFuture是一个接口,它继承了Runnable和Future这两个接口。RunnableFuture的源码如下:

    public interface RunnableFuture<V> extends Runnable, Future<V> {
        void run();
    }

    (02) FutureTask实现了RunnableFuture接口。所以,我们也说它实现了Future接口。

    示例和源码分析(基于JDK1.7.0_40)

    我们先通过一个示例看看Callable和Future的基本用法,然后再分析示例的实现原理。

    import java.util.concurrent.Callable;
    import java.util.concurrent.Future;
    import java.util.concurrent.Executors;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.ExecutionException;
    
    class MyCallable implements Callable {
    
        @Override 
        public Integer call() throws Exception {
            int sum    = 0;
            // 执行任务
            for (int i=0; i<100; i++)
                sum += i;
            //return sum; 
            return Integer.valueOf(sum);
        } 
    }
    
    public class CallableTest1 {
    
        public static void main(String[] args) 
            throws ExecutionException, InterruptedException{
            //创建一个线程池
            ExecutorService pool = Executors.newSingleThreadExecutor();
            //创建有返回值的任务
            Callable c1 = new MyCallable();
            //执行任务并获取Future对象 
            Future f1 = pool.submit(c1);
            // 输出结果
            System.out.println(f1.get()); 
            //关闭线程池 
            pool.shutdown(); 
        }
    }

    运行结果

    4950

    结果说明
      在主线程main中,通过newSingleThreadExecutor()新建一个线程池。接着创建Callable对象c1,然后再通过pool.submit(c1)将c1提交到线程池中进行处理并且将返回的结果保存到Future对象f1中。然后,我们通过f1.get()获取Callable中保存的结果;最后通过pool.shutdown()关闭线程池。(Mynote:线程池就是用来执行任务的。可以通过启动线程池中的线程来并发执行任务)

     

    1. submit()

    submit()在java/util/concurrent/AbstractExecutorService.java中实现,它的源码如下:

    复制代码
    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        // 创建一个RunnableFuture对象
        RunnableFuture<T> ftask = newTaskFor(task);
        // 执行“任务ftask”
        execute(ftask);
        // 返回“ftask”
        return ftask;
    }
    复制代码

    说明:submit()通过newTaskFor(task)创建了RunnableFuture对象ftask。它的源码如下:

    protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
        return new FutureTask<T>(callable);
    }

    2. FutureTask的构造函数

    它的实现原理还是基于AQS的。

    FutureTask的构造函数如下:

    复制代码
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        // callable是一个Callable对象
        this.callable = callable;
        // state记录FutureTask的状态
        this.state = NEW;       // ensure visibility of callable
    }
    复制代码

    3. FutureTask的run()方法

    我们继续回到submit()的源码中。
    在newTaskFor()新建一个ftask对象之后,会通过execute(ftask)执行该任务。此时ftask被当作一个Runnable对象进行执行,最终会调用到它的run()方法;ftask的run()方法在java/util/concurrent/FutureTask.java中实现,源码如下:

    复制代码
    public void run() {
        if (state != NEW ||
            !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                         null, Thread.currentThread()))
            return;
        try {
            // 将callable对象赋值给c。
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    // 执行Callable的call()方法,并保存结果到result中。
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                // 如果运行成功,则将result保存
                if (ran)
                    set(result);
            }
        } finally {
            runner = null;
            // 设置“state状态标记”
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
    复制代码

    说明:run()中会执行Callable对象的call()方法,并且最终将结果保存到result中,并通过set(result)将result保存。
          之后调用FutureTask的get()方法,返回的就是通过set(result)保存的值。

  • 相关阅读:
    关于JSON可能出现的错误,待更/todo
    mongoose的安装与使用(书签记录) 2017
    HTTP的学习记录3--HTTPS和HTTP
    HTTP的学习记录(二)头部
    HTTP(一)概述
    LeetCode 455. Assign Cookies
    LeetCode 453. Minimum Moves to Equal Array Elements
    LeetCode 448. Find All Numbers Disappeared in an Array
    LeetCode 447. Number of Boomerangs
    LeetCode 416. Partition Equal Subset Sum
  • 原文地址:https://www.cnblogs.com/Hermioner/p/9941528.html
Copyright © 2011-2022 走看看