Callable
通常来说,创建多线程的方式有两种,继承Thread或者实现Runnable接口。但是这两种方式中的run方法返回值都是void,当我们希望在线程执行完的时候返回一个结果时,我们可以使用实现Callable接口来创建线程。Callable接口的源码如下:
public interface Callable<V> {
V call() throws Exception;
}
接口中的call方法相当于Runnable中的run方法,只不过call会返回一个泛型值。
Future
Future是一个接口,用来获取一个异步处理的结果,源码如下:
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;
}
其中,
cancel方法是用来取消任务的执行,mayInterruptIfRunning参数是用来判断是否终止正在运行的任务,如果为true,就代表可以取消正在运行的任务,取消成功了返回true,如果为false,则表示不能终止正在运行的任务,必须等待其运行结束,返回false。如果任务已经结束,返回false。如果任务还没有开始,返回false。
isCancelled方法用来判断任务在完成前是否被取消,如果被取消了,则返回true,如果没有被取消,返回false。
isDone方法用来判断任务是否执行结束,无论是执行完成或者中途取消或者发生异常,都返回true,没有结束则返回false。
get方法用来阻塞式的获取任务执行的结果,没有执行结束就一直阻塞,一直到执行结束返回结果。
get方法带了timeout参数用来指定时间限制,如果超过了时间还没有返回结果则抛出异常。
FutureTask
FutureTask是一个类,因为Future只是一个接口,并不能拿来创建对象。FutureTask就是Future的实体类。
不过FutureTask是实现的RunnableFuture接口,而RunnableFuture接口是实现了Runnable接口和Future接口。
public class FutureTask<V> implements RunnableFuture<V> {
public interface RunnableFuture<V> extends Runnable, Future<V> {
void run();
}
可以看到FutureTask中也有run方法,所以可以使用线程池的execute方法让其执行,也可以使用Thread的start方法让其执行。
FutureTask有一个state属性,state的值有如下几种:
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
NEW表示任务刚刚被创建或者还没有被执行完的任务,是一个初始状态。在FutureTask被构造方法实例化后,会将state属性置为NEW。
COMPLETING表示任务已经执行完成或者执行过程中发生异常,但是执行结果或者异常报告还没有被保存到outcome字段中,outcome字段用来保存执行的结果或者异常的报告。COMPLETING是一个中间态,持续时间很短,最终将跳到NORMAL或EXCEPTIONAL或CANCELLED或INTERRUPTING或INTERRUPTED状态。
NORMAL表示任务已经执行完成,并且执行结果已经保存到outcome字段中,是一个最终态。
EXCEPTIONAL表示任务在执行过程发生异常,并且异常报告已经保存到outcome字段中,是一个最终态。
CANCELLED表示任务还没有执行时调用了cancel(false)方法使任务取消了,是一个最终态。
INTERRUPTING表示任务在执行还没有执行或者在执行过程中调用了cancel(false)方法要取消任务但是还没有取消完成,当取消完成了就跳到INTERRUPTED,是一个中间态。
INTERRUPTED表示任务在执行还没有执行或者在执行过程中调用了cancel(false)方法取消任务完成,是一个最终态。
其中,所有值大于COMPLETING的状态都表示任务已经执行完成(任务正常执行完成,任务执行异常或者任务被取消)。
代码应用:
public class FuturetaskTest {
public static void main(String[] args) {
FutureTask task = new FutureTask(new Callable() {
@Override
public Integer call() throws Exception {
Thread.sleep(1000);//模拟任务处理时间
System.out.println("任务已经启动!!!");
Integer result = 0;
for (int i = 0; i < 5000000; i++) {
result += i;
}
return result;
}
});
System.out.println("主线程在执行!");
//第一种方式,使用线程池调用
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.execute(task);
try {
System.out.println("任务是否完成:"+task.isDone());
Thread.sleep(2000);//让任务先执行
System.out.println("取消任务:"+task.cancel(false));
System.out.println("任务是否被取消:"+task.isCancelled());
System.out.println("任务执行结果:"+task.get());
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("主线程执行完毕!");
System.out.println("任务是否完成:"+task.isDone());
executor.shutdown();
//第二种方式,使用Thread调用
Thread thread = new Thread(task);
thread.start();
try {
System.out.println("任务执行结果:"+task.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
执行结果:
可以看到,在任务已经执行的时候调用cancel(false)方法并没有取消任务。而且在第二次尝试启动任务时,任务并没有启动,而是直接返回了第一次执行的结果。所以FutureTask的任务只会执行一次,在返回结果是会判断当前任务的state是否等于NEW,如果不为NEW则说明任务或者已经执行过,或者已经被取消,直接返回。
FutureTask使用场景
FutureTask可以用来异步获取处理结果,创建一个FutureTask来处理运算,在主线程的合适位置调用get获取处理结果,去除主线程的等待时间,将等待时间可以去处理其他复杂的业务逻辑。
还有在很多高并发的环境下,往往我们只需要某些任务只执行一次。这种使用情景FutureTask的特性恰能胜任。举一个例子,假设有一个带key的连接池,当key存在时,即直接返回key对应的对象;当key不存在时,则创建连接。