java Callable & Future & FutureTask

        实现Runnable接口的线程类与一个缺陷,就是在任务执行完以后没法取得任务的返回值。 若是须要获取执行结果,就必须经过共享变量或者使用线程通讯的方式来达到效果,这样使用起来就比较麻烦 。因此,从JDK 1.5开始,java提供了Callable接口,该接口和Runnable接口相相似,提供了一个call()方法能够做为线程的执行体,可是call()方法要比run()方法更为强大:# call()方法能够有返回值;# call()方法能够声明抛出异常。那么使用callable接口是如何获取返回值的呢?html

1、Callable与Runnable

          既然Callable接口能够看作是Runnable的“加强版”,那咱们先看看Runnable接口的实现,追根溯源也是搬砖的广泛素质嘛~java

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

Runnable接口中只包含一个抽象方法run()返回值为void, 因此在执行完任务以后没法返回任何结果。接下来咱们能够看看Callable接口编程

public interface Callable<V> {

    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */

    V call() throws Exception;
}

这是一个泛型接口,call()函数返回的类型就是传递进来的V类型。 那么该如何使用Callable接口呢?Callable接口不是Runnable接口的子接口,因此Callable对象不能直接做为Thread的target去运行;并且call方法还有返回值--call()方法并非直接调用。并发

    JDK提供了Future接口来表明call()方法里的返回值,而且为Future接口提供了一个实现类FutureTask,该类实现了Future接口,并实现了Runnable接口,其实该类也是Future接口的惟一实现类,因此FutureTask对象能够做为Thread的target。Callable通常配合ExecutorService来使用的,在ExecutorService接口中声明了若干个submit方法的重载版本:函数

/**
 * Submits a value-returning task for execution and returns a
 * Future representing the pending results of the task. The
 * Future's <tt>get</tt> method will return the task's result upon
 * successful completion.
 */
<T> Future<T> submit(Callable<T> task);

/**
 * Submits a Runnable task for execution and returns a Future
 * representing that task. The Future's <tt>get</tt> method will
 * return the given result upon successful completion.
 */
<T> Future<T> submit(Runnable task, T result);

/**
 * Submits a Runnable task for execution and returns a Future
 * representing that task. The Future's <tt>get</tt> method will
 * return <tt>null</tt> upon <em>successful</em> completion.
 */
Future<?> submit(Runnable task);

通常状况下咱们使用第一个submit方法和第三个submit方法,第二个submit方法不多使用 。spa

       这里提一下ExecutorService的submit与execute方法的区别:ExecutorService的submit与execute方法都能执行任务,但在使用过程,发现其对待run方法抛出的异常处理方式不同。二者执行任务最后都会经过Executor的execute方法来执行,但对于submit,会将runnable物件包装成FutureTask,其run方法会捕捉被包装的Runnable Object的run方法抛出的Throwable异常,待submit方法所返回的的Future Object调用get方法时,将执行任务时捕获的Throwable Object包装成java.util.concurrent.ExecutionException来抛出。
而对于execute方法,则会直接抛出异常,该异常不能被捕获,想要在出现异常时作些处理,能够实现Thread.UncaughtExceptionHandler接口。.net

        当将一个Callable的对象传递给ExecutorService的submit方法,则该call方法自动在一个线程上执行,而且会返回执行结果Future对象。一样,将Runnable的对象传递给ExecutorService的submit方法,则该run方法自动在一个线程上执行,而且会返回执行结果Future对象,可是在该Future对象上调用get方法,将返回null。线程

2、Future接口

Future就是对于具体的Runnable或者Callable任务的执行结果进行取消、查询是否完成、获取结果。必要时能够经过get方法获取执行结果,该方法会阻塞直到任务返回结果。code

Future类位于java.util.concurrent包下 :htm

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表示是否容许取消正在执行却没有执行完毕的任务,若是设置true,则表示能够取消正在执行过程当中的任务。若是任务已经完成,则不管mayInterruptIfRunning为true仍是false,此方法确定返回false,即若是取消已经完成的任务会返 回false;若是任务正在执行,若mayInterruptIfRunning设置为true,则返回true,若 mayInterruptIfRunning设置为false,则返回false;若是任务尚未执行,则不管 mayInterruptIfRunning为true仍是false,确定返回true。
  • isCancelled方法表示任务是否被取消成功,若是在任务正常完成前被取消成功,则返回 true。
  • isDone方法表示任务是否已经完成,若任务完成,则返回true;
  • get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
  • get(long timeout, TimeUnit unit)用来获取执行结果,若是在指定时间内,还没获取到结果,就直接返回null。

  也就是说Future提供了三种功能:

  1)判断任务是否完成;

  2)可以中断任务;

  3)可以获取任务执行结果。

由于Future只是一个接口,因此是没法直接用来建立对象使用的,所以就有了FutureTask。

3、FutureTask实现类

咱们先来看一下FutureTask的实现:

public class FutureTask<V> implements RunnableFuture<V>

FutureTask类实现了RunnableFuture接口,咱们再来看一下RunnableFuture接口的实现:

public interface RunnableFuture<V> extends Runnable, Future<V> {
    void run();
}

能够看出RunnableFuture继承了Runnable接口和Future接口,而FutureTask实现了RunnableFuture接口。因此它既能够做为Runnable被线程执行,又能够做为Future获得Callable的返回值

FutureTask提供了2个构造器:

public FutureTask(Callable<V> callable) {
}
public FutureTask(Runnable runnable, V result) {
}

如何有童鞋对FutureTask具体内容感兴趣,能够看一下这篇文章:FutureTask深刻解析

4、使用示例

1.使用Callable+Future获取执行结果 :

public class CallableTest implements Callable<String>{
	
	private int id;
	
	public CallableTest(int ThreadId){
		id = ThreadId;
	}
	
	public String call(){
		return "knock knock,who's there ? This is Thead " + id;
	}

	public static void main(String[] args) {
		ExecutorService exec = Executors.newCachedThreadPool();
		ArrayList<Future<String>> results = new ArrayList<Future<String>>();
		for(int i = 0; i < 10; i++){
			results.add(exec.submit(new CallableTest(i)));
		}
		for(Future<String> fs : results){
			try {
				System.out.println(fs.get());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}finally{
				exec.shutdown();
			}
		}
	}

}

执行结果:

knock knock,who's there ? This is Thead 0
knock knock,who's there ? This is Thead 1
knock knock,who's there ? This is Thead 2
knock knock,who's there ? This is Thead 3
knock knock,who's there ? This is Thead 4
knock knock,who's there ? This is Thead 5
knock knock,who's there ? This is Thead 6
knock knock,who's there ? This is Thead 7
knock knock,who's there ? This is Thead 8
knock knock,who's there ? This is Thead 9

其中submit方法会产生Future对象,用Callable返回结果的特定类型进行了参数化。固然,也能够用isDone()来查询Future是否已经完成。当任务完成时,能够调用get()方法来获取该结果;也能够直接调用get()方法,该种状况下,get()将阻塞,直至结果准备就绪。

2.使用Callable+FutureTask获取执行结果 :

public class CallableTest implements Callable<String>{
	
	private int id;
	
	public CallableTest(int ThreadId){
		id = ThreadId;
	}
	
	public String call(){
		return "knock knock,who's there ? This is Thead " + id;
	}

	public static void main(String[] args) {
		ArrayList<FutureTask<String>> results = new ArrayList<FutureTask<String>>();
		for(int i = 0; i < 10; i++){
			FutureTask<String> ft = new FutureTask<>(new CallableTest(i));
			new Thread(ft).start();
			results.add(ft);
		}
		for(Future<String> fs : results){
			try {
				System.out.println(fs.get());
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (ExecutionException e) {
				e.printStackTrace();
			}
		}
	}

}

执行结果:

knock knock,who's there ? This is Thead 0
knock knock,who's there ? This is Thead 1
knock knock,who's there ? This is Thead 2
knock knock,who's there ? This is Thead 3
knock knock,who's there ? This is Thead 4
knock knock,who's there ? This is Thead 5
knock knock,who's there ? This is Thead 6
knock knock,who's there ? This is Thead 7
knock knock,who's there ? This is Thead 8
knock knock,who's there ? This is Thead 9

能够看到,使用FutureTask对象来获取返回结果的时候,该对象能够做为Thread中的target对象,因此能够不使用 ExecutorService  来提交任务(实际上ExecutorService可能要比new Thread().start()方式要慢一点),但运行的结果仍是同样的。

 

参考文章:

Java并发编程:Callable、Future和FutureTask

java的Future和FutureTask

相关文章
相关标签/搜索