今天总结一下java中跟多线程相关的两个接口:Callable接口和Future接口。
我们知道,创建线程有两种方式,实现Thread类和实现Runnable接口。
但是这两种方法,run方法返回值类型均为void,在线程执行完任务后,无法获取执行结果。
可以通过Callable接口和Future接口获取执行完任务后的结果。
Callable接口
1 | public interface Callable<V> { |
可以看到Callable接口里面声明了一个call方法,返回的类型就是传递进来的类型V。
Callable接口的使用一般配合ExecutorService使用,将一个Callable或者Runnable对象传给ExecutorService的submit方法,返回一个Future的对象。
ExecutorService的重载的submit方法如下:
1 | <T> Future<T> submit(Callable<T> task); |
Future接口
Future接口保存异步计算的结果,可以对具体的Runnable或者Callable任务的执行结果进行取消、查询是否已完成、获取结果等操作。
Future接口的源码如下:
1 | public interface Future<V> { |
该接口声明了5个方法:
cancle 方法用来取消任务,如果取消任务成功则返回true,如果取消任务失败则返回false。参数mayInterruptIfRunning表示是否允许取消正在执行的任务。
isCancled方法表示任务是否被取消成功;
isDone方法表示任务是否已经完成;
get()方法用来获取执行结果,这个方法会产生阻塞,会一直等待直到任务执行完毕才返回;
get(long timeout, TimeUnit unit)用来获取执行结果,如果在指定时间内,还没获取到结果,就直接返回null。
###FutureTask
FutureTask是Future接口的唯一实现类,其实现如下:
1 | public class FutureTask<V> implements RunnableFuture<V> |
通过源码可以看到,RunnableFuture继承了Runnable和Future接口,所以FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。
使用方法
Callable+FutureTask获取线程执行结果
1 | //构造对象实现Callable接口 |
执行结果:
1 | compute thread! |
main函数也可以通过 ExecutorService来起线程:
1 | package com; |
使用Callable和Future获取执行结果
一般配合ExecutorSercie使用:
1 | package com; |
执行器
在上面通过Callable和Future获取线程返回结果的时候,我们已经用到了线程池的概念,通过传入一个Callable对象到线程池起线程。
因为创建线程涉及与操作系统的交互,会存在一定的开销,如果程序中创建了大量的生命周期很短的线程,会影响程序的性能,这个时候可以使用线程池。
一个线程池中包含许多运行的空闲线程,将Runnable对象交个线程池,就会有一个线程调用run方法,当run方法退出时,线程不会死亡,而是在线程池中准备为下一个请求提供服务。
通过执行器Executors的静态方法创建线程池,Executors累包含如下静态方法:
1 | newCachedThreadPool;//构建一个线程池,如果有空闲线程可用,则让它执行任务,如果没有空闲线程,则创建一个空线程; |
线程池使用步骤:
- 调用Executors类中的静态方法创建线程池;
- 调用submit提交Runnable或Callable对象;
- 如果想要取消一个任务,或如果提交Callable对象,就保存好返回的Future对象;
- 当不需要提交任务时,调用shutdown。