美文网首页
java中的 Future详解以及ExecutorService

java中的 Future详解以及ExecutorService

作者: 和帅_db6a | 来源:发表于2018-11-26 19:49 被阅读0次

    这一块内容和线程池也是息息相关的
    线程池的顶级接口是Executor接口,里面只有一个未实现方法是

    void execute(Runnable command);
    

    下来是ExecutorService接口,继承自Executor接口,里面多 了很多方法,比较重要的几个方法是

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
           throws InterruptedException;
    

    提交多个任务,并返回与每个任务对应的Futue。也就是说,任务彼此之间不会相互影响,可以通过future跟踪每一个任务的执行情况,比如是否被取消,是正常完成,还是异常完成。调用该方法的线程会阻塞,直到tasks全部执行完成(正常完成/异常退出)
    如果线程在等待invokeAll执行的过程中被中断,那么线程池就会终止所有正在被执行的任务,并抛出异常。

    <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
                                    long timeout, TimeUnit unit)
          throws InterruptedException;
    

    给定的超时期满,还没有完成的任务会被取消,即Future.isCancelled()返回true;在超时期之前,无论是正常完成还是异常终止的任务,Future.isCancelled()返回false。

     <T> T invokeAny(Collection<? extends Callable<T>> tasks)
            throws InterruptedException, ExecutionException;
    

    提交多个任务,一旦有一个任务完成,就会终止其他任务的执行,如果没有一个任务完成,那么就会抛出异常。

        <T> T invokeAny(Collection<? extends Callable<T>> tasks,
                        long timeout, TimeUnit unit)
            throws InterruptedException, ExecutionException, TimeoutException;
    

    如果在超时之前,所有任务已经都是异常终止,那就没有必要在等下去了;如果超时之后,仍然有正在运行或等待运行的任务,那么会抛出TimeoutException。

     void shutdown();
    

    终止当前线程池,但是执行中的任务会执行到结束,等待的任务不被执行

        List<Runnable> shutdownNow();
    

    终止当前线程池,执行中的任务立即结束,等待的任务不被执行

    boolean isShutdown();
    

    如果调用了上面的两个shutdown方法,就返回true

     boolean isTerminated();
    

    如果在shutdown之后,所以任务也都结束了,线程池处于终结状态,那么返回true

     <T> Future<T> submit(Runnable task, T result);
    <T> Future<T> submit(Callable<T> task);
      Future<?> submit(Runnable task);
    

    上面的三个方法都是在线程池中提交一个任务,包括callable类型或者是runable类型。返回future对象
    AbstractExecutorService抽象类实现了ExecutorService,对上面的submit方法也有了实现,其最终还是在调用顶级接口Executor中的execute方法,但是AbstractExecutorService并没有实现execute方法,该方法在他的实现类ThreadPoolExecutor中实现。
    从上面可以看到submit的参数类型有两种分别是runable和callable,但是在AbstractExecutorService中对submit实现上,都是将两种对象转化成了FutureTask对象,然后将这个转化之后的对象传入execute方法中。
    为啥runnable对象和callable对象可以转化成futureTask对象呢?下面从Future接口说起。这个接口的方法如下:
    1.cancel方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行却没有执行完毕的任务,如果设置true,则表示可以取消正在执行过程中的任务。如果任务已经完成,则无论mayInterruptIfRunning为true还是false,此方法肯定返回false,即如果取消已经完成的任务会返回false;如果任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若mayInterruptIfRunning设置为false,则返回false;如果任务还没有执行,则无论mayInterruptIfRunning为true还是false,肯定返回true。
    2.isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true。
    3.isDone方法表示任务是否已经完成,若任务完成,则返回true;
    4.get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
    5.get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
    之后RunnableFuture接口继承了Runnable接口和Future接口,重点,继承了两个接口,然后FutureTask类是RunnableFuture的实现类。
    FutureTask的构造方法有两个,一个接受Callable对象,另一个接受runnable对象,但是接受了runnable对象之后会调用Executors.callable()方法,将这个runnable对象转化成callable对象,具体的转化就是通过一个RunnableAdapter类生成一个callabled对象,然后这个callable对象的call方法就是runnable的run方法。那就不用想也明白了,FutureTask中的实现的run方法一定是执行的直接传进来的callable对象或者转化来的call able对象的call方法。事实也是这样。
    那这个callable到底是什么,怎么这么牛逼啊,runnable传进来还要转化成他。

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

    因为call方法执行完可以返回参数,就这么简单,而run的返回是void。ok,正点来了,当我们起一个线程去做一个任务的时候调用run方法是没有返回值的,如果我们需要的话就只能用callable了。因为call方法是可以返回结果的。
    回头再看submit方法,传入一个runnable或者callable,开启一个新的线程去执行,然后返回一个future对象,用来对任务进行操作,是不是很牛逼的设计。
    这里上一个例子看看,这个例子就是用线程池加上future来实现多线程的一个方式。

    public class FutureDemo {
        //创建一个容量为1的线程池
        static ExecutorService executorService = Executors.newFixedThreadPool(1);
        public static void main(String[] args) throws Exception {
            //创建线程并提交线程,同时获取一个future对象
            Thread subThread = new Thread(new SubThread());
            Future future = executorService.submit(subThread);
            //主线程处理其他工作,让子线程异步去执行
            mainWork();
            //阻塞,等待子线程结束
            future.get();
            System.out.println("Now all thread done!");
            //关闭线程池
            executorService.shutdown();
        }
        //主线程工作
        private static void mainWork(){
            System.out.println("Main thread start work!");
            try {
                Thread.sleep(2000L);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("Main Thread work done!");
        }
        /**
         * 子线程类
         * @author fuhg
         */
        private static class SubThread implements Runnable{
            public void run() {
                // TODO Auto-generated method stub
                System.out.println("Sub thread is starting!");
                try {
                    Thread.sleep(5000L);
                } catch (InterruptedException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                System.out.println("Sub thread is stopping!");
            }
        }
    }
    

    当然如果要用future实现多线程并不是一定要用线程池,只是ExecutorService中的submit可以直接返回future对象,如果我们自己定义future对象的话,就可以不用线程池来实现。

    Callable<String> onlineShopping = new Callable<String>() {
    
                @Override
                public Stringcall() throws Exception {
                    System.out.println("第一步:下单");
                    System.out.println("第一步:等待送货");
                    Thread.sleep(5000);  // 模拟送货时间
                    System.out.println("第一步:快递送到");
                    return "ok";
                }       
            };
       FutureTask<String> task = new FutureTask<String>(onlineShopping);
       new Thread(task).start();
    

    这里我们自己定义了futuretask对象,然后直接作为参数传递给thread并运行也是ok的,对任务的操作直接用我们自己定义的task就行了。

    Callable<String> callable=new Callable<String>() {
                @Override
                public String call() throws Exception {
                    // TODO Auto-generated method stub
                    System.out.println("start  call");
                    Thread.sleep(5000);
                    System.out.println("end  call");
                    return "hello word";
                }
            };
            ExecutorService executorService = Executors.newFixedThreadPool(1);
               //创建线程并提交线程,同时获取一个future对象    
            Future future = executorService.submit(callable);
            //主线程处理其他工作,让子线程异步去执行
            //mainWork();
            //阻塞,等待子线程结束
            try {
                System.out.println(future.get());//获取子线程执行结束之后返回的结果
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            System.out.println("Now all thread done!");//如果调用了get但是子线程还没有执行结束,那么主线程就会阻塞,那么这句话要在子线程
      //执行结束之后,主线程开始执行才能输出。
    

    futuretask有一个重要的属性就是state

    private volatile int state; // 注意volatile关键字
        /**
         * 在构建FutureTask时设置,同时也表示内部成员callable已成功赋值,
         * 一直到worker thread完成FutureTask中的run();
         */
        private static final int NEW = 0; 
        /**
         * woker thread在处理task时设定的中间状态,处于该状态时,
         * 说明worker thread正准备设置result.
         */
        private static final int COMPLETING = 1;
     
        /**
         * 当设置result结果完成后,FutureTask处于该状态,代表过程结果,
         * 该状态为最终状态final state,(正确完成的最终状态)
         */
        private static final int NORMAL = 2;
        /**
         * 同上,只不过task执行过程出现异常,此时结果设值为exception,
         * 也是final state
         */
        private static final int EXCEPTIONAL = 3; 
        /**
         * final state, 表明task被cancel(task还没有执行就被cancel的状态).
         */
        private static final int CANCELLED = 4;
        /**
         * 中间状态,task运行过程中被interrupt时,设置的中间状态
         */
        private static final int INTERRUPTING = 5; 
        /**
         * final state, 中断完毕的最终状态,几种情况,下面具体分析
         */
        private static final int INTERRUPTED = 6;
    

    然后还给出了四种可能的结果

    Possible state transitions:
    * NEW -> COMPLETING -> NORMAL
    * NEW -> COMPLETING -> EXCEPTIONAL
    * NEW -> CANCELLED
    * NEW -> INTERRUPTING -> INTERRUPTED
    futuretask的cancel方法,get方法都会应用到这些状态。其中get方法会对调用线程进行阻塞。

    相关文章

      网友评论

          本文标题:java中的 Future详解以及ExecutorService

          本文链接:https://www.haomeiwen.com/subject/krriqqtx.html