Java多线程——Callable、Future和FutureTask

时间:2022-09-04 17:28:48

通过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的使用