zoukankan      html  css  js  c++  java
  • java中Future与FutureTask使用与分析

    Future与FutureTask都是用于获取线程执行的返回结果。下面我们就对两者之间的关系与使用进行一个大致的介绍与分析

    一、Future与FutureTask介绍

    Future位于java.util.concurrent包下,它是一个接口

    public interface Future<V> {
        boolean cancel(boolean mayInterruptIfRunning);
        boolean isCancelled();
        boolean isDone();
        V get() throws InterruptedException, ExecutionException;
        V get(long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    }

    Future接口中声明了5个方法,下面介绍一下每个方法的作用:

    cancel方法用来取消任务,取消成功则返回true,取消失败则返回false。参数mayInterruptIfRunning设置为false,表示不允许在线程运行时中断,设置为true则表示允许。具体可分为以下三种情况:

            1、如果任务已经完成,则无论mayInterruptIfRunning为true还是false,都返回false,这是因为你要取消的任务已经完成,则认为取消任务失败;

            2、如果任务正在执行,则无论mayInterruptIfRunning为true还是false,都返回true。只不过mayInterruptIfRunning为true时线程会被中断,false时线程不会被中断会执行完。

            3、如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,都返回true。

    isCancelled方法用于判断任务是否被取消成功,cancel方法成功则返回 true,反之则为false。

    isDone用于判断任务是否完成,  如果任务完成则返回true。任务完成包括正常结束、任务被取消、任务发生异常,都返回true

    get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;

    get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,抛出 java.util.concurrent.TimeoutException 异常 

    FutureTask 实现了RunnableFuture接口,而RunnableFuture则继承了Future<V>与Runnable接口,所以 FutureTask不仅实现了 Future<V>接口的所有方法,还具有自己的run方法,我们可以看下它的类图

    、Future与FutureTask使用与分析

    1、使用Future时,我们需要实现Callable接口,并通过ExecutorService接口的submit方法获取返回的Future对象,

    2、使用FutureTask时,根据FutureTask的构造函数可以看到FutureTask既可以接收Callable的实现类,也可以接收Runnable的实现类。当你传入的是Callable的实现类时,可以获取线程执行的结果;传入Runnable的实现类时,由于Runnable的实现没有返回值,需要传入一个你设置的线程完成标识,也就是result,然后当线程结束时会把你传入的result原值返回给你,FutureTask的构造函数具体如下:

        public class FutureTask<V> implements RunnableFuture<V>public FutureTask(Callable<V> callable) {
            if (callable == null)
                throw new NullPointerException();
            this.callable = callable;
            this.state = NEW;       // ensure visibility of callable
         }
    
         public FutureTask(Runnable runnable, V result) {
            this.callable = Executors.callable(runnable, result);//runnable转化为callable 
            this.state = NEW;       // ensure visibility of callable
         }
    }

     接下来我们看下Future与FutureTask具体的使用代码:

            // 执行任务 实现Runnable
            FutureTaskJobRunnable taskRun = new FutureTaskJobRunnable();
            // 执行任务 实现Callable
            FutureTaskJobCallable taskCall = new FutureTaskJobCallable();
            String val = "ok";
            // 线程运行成功后把,返回你传入的val值
            FutureTask<String> futureTaskRun = new FutureTask<String>(taskRun, val);
            // 线程运行,返回线程执行的结果
            FutureTask<String> futureTaskCall = new FutureTask<String>(taskCall);
            
            //声明线程池
            ExecutorService executor = Executors.newCachedThreadPool();        
            //Future
            Future<String> future =  executor.submit(taskCall);
            System.out.println(future.get());
            //FutureTask
            executor.submit(futureTaskCall);
            System.out.println(futureTaskCall.get());
            //FutureTask自定义线程执行
            new Thread(futureTaskRun).start();
            System.out.println(futureTaskRun.get());
    public class FutureTaskJobCallable implements Callable<String>{
        
        public String call() throws Exception {
            System.out.println("FutureTaskJobCallable已经执行了哦");
            Thread.sleep(1000);
            return "返回结果";
        }
    
    }
    
    public class FutureTaskJobRunnable implements Runnable {
        
        public void run() {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("FutureTaskJobRunnable已经执行了哦");
        }
    
    }

    根据上面的代码我们从ExecutorService接口中submit方法入手,看下AbstractExecutorService类对submit方法的具体实现。

        public Future<?> submit(Runnable task) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<Void> ftask = newTaskFor(task, null);
            execute(ftask);
            return ftask;
        }
    
        public <T> Future<T> submit(Runnable task, T result) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<T> ftask = newTaskFor(task, result);
            execute(ftask);
            return ftask;
        }
    
        public <T> Future<T> submit(Callable<T> task) {
            if (task == null) throw new NullPointerException();
            RunnableFuture<T> ftask = newTaskFor(task);
            execute(ftask);
            return ftask;
        }
    
        protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
            return new FutureTask<T>(runnable, value);
        }
    
    
        protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
            return new FutureTask<T>(callable);
        }

    可以看到当你使用submit方法提交任务时,都会通过newTaskFor方法转换成FutureTask对象,所以我们具体分析下上面代码中的三种情况:

    1、如果你传入的是自己实现的Runaable类或者Callable类,那么sumbit方法自然会帮你自动封装为FutureTask对象,运行后通过Future对象获取结果。

    2、你传入的已经是个自己构造的FutureTask对象,由于FutureTask其实是实现了Runnable接口的,它本身就是个Runaable实现类, sumbit方法还是会将它视为Runnable类来进行封装,并最终会执行FutureTask自己的run方法,一系列实现都在你传入的FutureTask对象内完成,所以你可以直接通过自己构建的FutureTask获取结果;

    3、自己单独声明线程运行,跟第2点类似,FutureTask本身就是个Runnabel实现类,自然可以做为参数传入Thread运行;

    那么我们把自定义的Runnable、Callable实现类做为参数构造FutureTask后,FuttureTask是如何运行的呢,我们可以看下FuttureTask中具体的代码实现

    //你传入的Runnable与Callable实现类都会在构造函数中转化为Callable
    private Callable<V> callable;
    
        public void run() {
            if (state != NEW ||
                !UNSAFE.compareAndSwapObject(this, runnerOffset,
                                             null, Thread.currentThread()))
                return;
            try {
                Callable<V> c = callable;//你传入的实现类
                if (c != null && state == NEW) {
                    V result;//返回值
                    boolean ran;
                    try {
                        result = c.call();//运行后返回结果
                        ran = true;
                    } catch (Throwable ex) {
                        result = null;
                        ran = false;
                        setException(ex);
                    }
                    if (ran)
                        set(result);
                }
            } finally {
                // runner must be non-null until state is settled to
                // prevent concurrent calls to run()
                runner = null;
                // state must be re-read after nulling runner to prevent
                // leaked interrupts
                int s = state;
                if (s >= INTERRUPTING)
                    handlePossibleCancellationInterrupt(s);
            }
        }

    可以看到FutureTask类本身的run方法,就是执行Runnable、Callable的实现类并获取返回结果的过程。

    所以ExecutorService接口中submit方法归根结底还是要把你传入的对象封装成FutureTask对象,并通过FutureTask类的内部实现来获取结果的,返回的Future接口对象也要依赖于FutureTask实例化的,所以无论是直接传入自己的Runnable、Callable实现类还是构建FutureTask传入,本质上都是通过FutureTask去实现,没有什么区别;

    以上就是对Future与FutureTask两者关系的一个大致的介绍与分析,其中如有不足与不正确的地方还望指出与海涵。

    关注微信公众号,查看更多技术文章。

  • 相关阅读:
    JavaScript对象编程-第3章
    JavaScript对象编程-第3章
    JavaScript基础-第2章
    JavaScript基础-第2章
    JavaScript基础-第2章
    第十八节:详解Java抽象类和接口的区别
    第十八节:详解Java抽象类和接口的区别
    第十八节:详解Java抽象类和接口的区别
    JavaScript概述-第1章
    JavaScript概述-第1章
  • 原文地址:https://www.cnblogs.com/dafanjoy/p/9778271.html
Copyright © 2011-2022 走看看