实现Runnable接口的线程类与一个缺陷,就是在任务执行完以后没法取得任务的返回值。 若是须要获取执行结果,就必须经过共享变量或者使用线程通讯的方式来达到效果,这样使用起来就比较麻烦 。因此,从JDK 1.5开始,java提供了Callable接口,该接口和Runnable接口相相似,提供了一个call()方法能够做为线程的执行体,可是call()方法要比run()方法更为强大:# call()方法能够有返回值;# call()方法能够声明抛出异常。那么使用callable接口是如何获取返回值的呢?html
既然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。线程
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个方法,下面依次解释每一个方法的做用:
也就是说Future提供了三种功能:
1)判断任务是否完成;
2)可以中断任务;
3)可以获取任务执行结果。
由于Future只是一个接口,因此是没法直接用来建立对象使用的,所以就有了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深刻解析
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()方式要慢一点
),但运行的结果仍是同样的。
参考文章: