java中的 Future详解(以及ExecutorService中的各种方法)

关于这一块一前一直是一个盲点,看了看源码总结一下把。这一块内容和线程池也是息息相关的

线程池的顶级接口是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<Chuju> onlineShopping = new Callable<Chuju>() {

            @Override
            public Chuju call() throws Exception {
                System.out.println("第一步:下单");
                System.out.println("第一步:等待送货");
                Thread.sleep(5000);  // 模拟送货时间
                System.out.println("第一步:快递送到");
                return new Chuju();
            }
            
        };
   FutureTask<Chuju> task = new FutureTask<Chuju>(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方法会对调用线程进行阻塞。


暂时就这么多把,太深层次的我也看不懂了。























猜你喜欢

转载自blog.csdn.net/u010365819/article/details/80761332