• 深入并发之(三) FutureTask与Future之写线程池的时候到底需不需要自己包装FutureTask


    写线程池的时候到底需不需要自己包装FutureTask

    一般来说,我们会写一个Runnable接口的实现用来包装我们需要异步执行的内容。但是,Runnable是没有实现的,有些时候,我们需要执行的内容是一个有返回值的方法或者需要抛出异常,这个时候,我们就需要使用Callable接口。

    简单来说,callable可以实现所有Runnable能够实现的功能。

    由于我们需要在线程异步执行完成后返回一个结果,但是由于这个执行是异步的,那么,我们是没有办法立刻拿到结果的,这时Future方式就出现了。

    Future实际上一个承诺,当我们调用时,返回一个Future对象,这个返回是立刻的,当线程的异步任务完成时,我们可以通过方法Future.get()来获取真正的返回值。

    在代码的编写过程中,我们可能见过以下两种例子

    示例1

    FutureTask<String> task = new FutureTask<String>(new MyCallable());
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    
    executorService.submit(task);
    
    String s = task.get();
    
    System.out.println(s);
    
    executorService.shutdown();
    

    示例2

    MyCallable callable = new MyCallable();
    
    ExecutorService executorService Executors.newFixedThreadPool(5);
    
    Future<String> future executorService.submit(callable);
    
    String s = future.get();
    
    System.out.println(s);
    
    executorService.shutdown();
    
    

    我们发现这两种方式实际上是相同的结果,这是我们就会十分迷惑,FutureTask与Future有什么区别?

    首先,我们需要了解,Future实际上是一个接口,这个接口中有如下方法。

    futureMethod

    FutureTask实际上是实现了接口RunnableFuture,而RunnableFuture继承了Runnable, Future

    public class FutureTask<V> implements RunnableFuture<V>
    
    public interface RunnableFuture<V> extends Runnable, Future<V> 
    

    所以,我们知道实际上FutureTask实际上既是一个Future也是一个Callable,因此,它可以作为线程池的一个任务,通过submit(Callable)方法提交给线程池进行处理。

    一般来说,我们不需要对Callable进行包装,也就是上面示例1的方式并不是我们通常应该选择的方式。因为,实际上在线程池中已经将Callable包装为FutureTask了。

    这里我们从源码中分析一下我们给线程池执行的Callable到底是怎么执行的?

    首先,我们需要查看submit方法

    查看ThreadPoolExecutor类,没有找到submit方法,那么这个方法一定是在父类中

    public class ThreadPoolExecutor extends AbstractExecutorService
    

    接下来我们查看抽象类AbstractExecutorService

    public <T> Future<T> submit(Callable<T> task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<T> ftask = newTaskFor(task);
        execute(ftask);
        return ftask;
    }
    

    这里我们注意到在task执行前,有一个方法newTaskFor的调用,这个方法返回了RunnableFuture类的对象

    public interface RunnableFuture<V> extends Runnable, Future<V> 
    

    FutureTask实际上是这里的RunnableFuture的接口

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

    我们看到newTaskFor方法,实际上返回的就是FutureTask,因此,我们根本无需再将Callable包装为FutureTask,这个工作实际上重复的。

    接下来的一篇中,我们将会分析真正执行任务的方法execute(ftask);

  • 相关阅读:
    go http的三种实现---2
    go http的三种实现---1
    go语言递归创建目录
    Golang中的正则表达式
    go语言strings包
    go语言获取字符串元素的个数
    VBA在Excel中的应用(三)
    ASP 转换HTML特殊字符
    ASP汉字转拼音函数的方法
    用ASP实现文件下载
  • 原文地址:https://www.cnblogs.com/qmlingxin/p/9631874.html
Copyright © 2020-2023  润新知