通过Thread或Runnable创建的线程,都需要重写run方法,而run方法的返回是void的,所以使用这种方式无法获取线程执行结果。但java提供了其他类和方法来获取线程执行结果,主要的类有Callable、Future和FutureTask。
Callable
Callable是个泛型接口 Callable ,该接口中只有个call()方法,并且返回值也为 V,常和ExecutorService中的 submit 方法配合使用。
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
Future
Future可以对具体的 Runnable 或者 Callable 任务的执行结果进行取消、查询是否完成、获取结果等操作。可以通过get方法获取执行结果,该方法会阻塞直到任务返回结果。
Future 接口中有5个方法:
cancel()方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false;
isCancelled方法表示任务是否被取消成功,如果在任务正常完成前被取消成功,则返回 true;
isDone方法表示任务是否已经完成,若任务完成,则返回true;
get()方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
FutureTask
FutureTask类实现了 RunnableFuture 接口,RunnableFuture 继承了 Runnable 接口和 Future 接口,所以FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
FutureTask提供了2个构造器:
public FutureTask(Callable<V> callable) {
}
所以实现了Callable的对象可以通过该构造器得到一个FutureTask,而FutureTask可以直接给线程来执行,然后通过FutureTask取回执行结果。
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
可以看出该构造器通过 Executors.callable()
把Runnable转换为Callable,其内部使用了适配器 RunnableAdapter。因此FutureTask实现了Future、Runnable,又是包装了Callable( 如果是Runnable最终也会被转换为Callable ), 它是这两者的合体。
示例代码
ExecutorService的两个方法:
<T> Future<T> submit(Callable<T> task);
Future<?> submit(Runnable task);
可以看出,通过第一个方法直接执行Callable,然后从Future中获取结果;或者用 FutureTask(Callable callable) 构造个FutureTask(它实现了Runable接口)对象用第二个方法获取返回结果,或者直接就在一个线程中执行。
代码实现:
public class ThreadCallablePractice {
public void callableTest() throws Exception{
ExecutorService executorService = Executors.newFixedThreadPool(3);
Future<String> future = executorService.submit(new Task());
executorService.shutdown();
System.out.println(future.get());
}
public void futureTaskTest() throws Exception{
FutureTask<String> futureTask = new FutureTask<String>(new Task());
//new Thread(futureTask).start();
//或者仍用线程池
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(futureTask);
executorService.shutdown();
System.out.println(futureTask.get());
}
public static void main(String[] args) throws Exception{
ThreadCallablePractice practice = new ThreadCallablePractice();
//practice.callableTest();
practice.futureTaskTest();
}
}
class Task implements Callable<String>{
public String call(){
System.out.println(Thread.currentThread().getName() + " is working");
return "callable result";
}
}
运行结果
pool-1-thread-1 is working
callable result
参考资料
Java并发编程:Callable、Future和FutureTask
Callable和Future、FutureTask的使用