Java中的Future和FutureTask探索

什么时候要用到Future?

当程序的执行需要依赖于另一个线程的执行完成或计算结果时,这时候就需要线程阻塞等待另一个线程的执行。Future的get()方法会阻塞当前线程,直到另一个线程执行完毕并返回结果。

什么是Future?

Future是一个接口,提供了一些方法定义,用于控制任务的执行及获取执行状态及结果,源码如下:

public interface Future<V> {

	/**
    * 取消执行
    * 参数表示是否允许取消正在执行的任务
    */
    boolean cancel(boolean mayInterruptIfRunning);
    
    /**
    * 是否已取消执行
    */
    boolean isCancelled();
    
     /**
    * 是否已完成执行
    */
    boolean isDone();
    
      /**
    * 获取执行结果(阻塞)
    */
    V get() throws InterruptedException, ExecutionException;

	 /**
    * 特定时间后获取执行结果,如果到时未执行完毕返回null
    */
    V get(long timeout, TimeUnit unit)  throws InterruptedException, ExecutionException, TimeoutException;
    
}

通过Future获取工作线程执行结果
public static void main(String[] args) {

        System.out.println("主线程开始执行。。。");
        ExecutorService executorService = Executors.newCachedThreadPool();
        Future<Integer> future = executorService.submit(new Task());
        try {
            Integer integer = future.get();
            System.out.println("获取工作线程执行结果:" + integer);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("主线程执行完毕。。。");
        
 }

static class Task implements Callable<Integer> {

        @Override
        public Integer call() throws Exception {
            System.out.println("工作线程开始执行。。。");
            int value = 0;
            for (int i = 0; i < 5; i++) {
                value++;
                System.out.println(value);
                Thread.sleep(1000);
            }
            System.out.println("工作线程执行完毕。。。");
            return value;
        }
    }

执行打印结果:

主线程开始执行。。。
工作线程开始执行。。。
1
2
3
4
5
工作线程执行完毕。。。
获取工作线程执行结果:5
主线程执行完毕。。。

上述示例中
通过线程池submit()方法开启工作线程任务的执行,实际上这个方法还有一些重载的方法,不仅可以传人Callable还可以传入Runnable,只不过Callable可以直接通过泛型确定返回的数据类型,Runnable则稍显麻烦。从下面重载的方法来看,要么直接传入Runnable,要么调用两个参数的重载方法,给该工作线程一个固定的返回结果,但是无论哪种方式都没办法将run()方法内的程序计算结果获取到,因为run()方法没有返回值。而Callable的call()方法则可以返回程序的计算结果。

public interface Runnable {
    public abstract void run();
}

public interface Callable<V> {
    V call() throws Exception;
}
public abstract class AbstractExecutorService implements ExecutorService {

    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;
    }
}
什么是FutureTask

FutureTask从类名上就可以看出来是对工作任务的封装,同时联系到Future那么它应该还包含了Future的一些特性。它有两个构造方法,可以传入Callable,可以传入Runnable,Runnable最终也会被封装成Callable:

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);
     	   this.state = NEW;       // ensure visibility of 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);
        }
    }
    
    ......
}
public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

FutureTask是RunnableFuture的实现类,看一下RunnableFuture的定义,RunnableFuture接口同时继承了Runnable和Future,所以FutureTask本质上其实是对Runnable接口的扩展,也是对构造方法传入的Runnable或Callable对象的进一步封装,增加了对执行任务的控制及状态记录。

既然是作为Runnable来使用那么它的任务调用逻辑一定是在自身对run()实现中,从上面源码中可以看到run()方法内部实际上是通过Callable的call方法实现的,这样就达到了获取返回值的目的。

通过FutureTask获取工作线程执行结果

对于上述示例我们看一下通过FutureTask获取其执行结果

 public static void main(String[] args) {
 
        System.out.println("主线程开始执行。。。");
        ExecutorService executorService = Executors.newCachedThreadPool();
        FutureTask<Integer> futureTask = new FutureTask<>(new Task());
        executorService.submit(futureTask);
        
        try {
            Integer integer = futureTask.get();
            System.out.println("获得子线程执行结果:" + integer);
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("主线程执行完毕。。。");
        
 }

本质上是把FutureTask当成了Runnable传递给ExecutorService接口的submit()方法,前面提到submit()方法有三个不同的重载方法,那么看一下他们有什么区别,submit()具体实现是在抽象类AbstractExecutorService中:

public abstract class AbstractExecutorService implements ExecutorService {

	 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()的方法从其实现上来看,不管传入的是Runnable还是Callable最终都是被封装成FutureTask,最后交由ExecutorService.execute执行。

因此通过Future获取工作线程的执行结果,本质上还是通过FutureTask.get方法来实现的(从源码中可以很清晰的看到,get()方法内部会维护一个无限的for循环阻塞当前线程,轮询Task的执行状态,直到获任务完成、线程被打断或者出现异常后终止循环返回执行结果)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值