Callable、Runnable,Future和FutureTask之间关系
创建线程的两种方式:继承Thread类 或 实现Runnable接口,重写run方法。
Thread类本身也实现了Runnable接口,Runnable接口源码:
run方法是无返回值的,所以在JDK1.5出现了Callable接口
关系类图
Callable
Callable接口源码
Callable是一个函数式接口(接口中仅有一个方法),也是一个泛型接口,返回值类型和泛型一致
Future
Future接口源码
cancel:取消任务的执行,如果任务已完成或已被取消,则返回false
isCancelled:判断任务是否被取消
isDone:判断任务是否完成
get():阻塞获取任务的执行结果
get(long timeout, TimeUnit unit):在规定的时间内,阻塞获取任务的执行结果
Future接口提供了取消任务,任务状态查询,任务结果获取的能力;
Future机制就是为了解决多线程返回值的问题;
RunnableFuture
RunnableFuture接口源码
RunnableFuture继承了Runnable和Future两个接口,也就同时具备其两个接口的功能
FutureTask
FutureTask是真正工作的处理类,实现了RunnableFuture接口,而RunnableFuture接口继承了Runnable和Future接口,所以FutureTask既可以作为Runnable被Thread执行,也可以获取Future异步执行的结果;
FutureTask两个构造方法,一个接收Callable的参数实例,另一个接收Runnable的参数实例
当传入的参数是Runnable时,通过Executors.callable(runnable, result)方法将其转成Callable类型(最终都是执行Callable类型的任务),返回值类型为V(指定的泛型类型)
RunnableAdapter适配器
FutureTask-demo示例
ExecutorService线程池接口中,sumbit方法即定义了Runnable入参类型,也定义了Callable入参类型
package com.example.demo.test; import java.util.concurrent.*; public class RunnableFutureTest { private static ExecutorService pool = Executors.newFixedThreadPool(2); public static void main(String[] args) throws Exception { testFuture(20); testRunnable(20); } /** * new Thread().start()新建一个线程,启动线程(实际执行run方法,无返回值) */ static void testRunnable(int number) { new Thread(new Runnable() { @Override public void run() { System.out.println("runnable sum:" + calcOneToTargetSum(number)); } }).start(); } /** * Runnable:实现run(),无返回值,不可以抛出异常 * Callable:实现call(),有返回值,可以抛出异常 * Runnable可以直接交给Thread来执行 * Callable不可以直接交给Thread来执行,一般交给ExecutorService执行 */ static void testFuture(int number) { try { Future<?> result1 = pool.submit(new Runnable() { @Override public void run() { calcOneToTargetSum(number); } }); // 无返回值,get()会阻塞 System.out.println("result1:" + result1.get()); Future<Integer> result2 = pool.submit(new Callable<Integer>() { @Override public Integer call() throws Exception { return calcOneToTargetSum(number); } }); // 有返回值,get()会阻塞 System.out.println("result2:" + result2.get()); FutureTask<Integer> futureTask1 = new FutureTask<>(new Runnable() { @Override public void run() { calcOneToTargetSum(number); } }, calcOneToTargetSum(number)); pool.submit(futureTask1); // 有返回值,get()会阻塞 System.out.println("result3:" + futureTask1.get()); FutureTask<Integer> futureTask2 = new FutureTask<>(new Runnable() { @Override public void run() { calcOneToTargetSum(number); } }, calcOneToTargetSum(number)); pool.submit(futureTask2); // Executors.callable会将Runnable转换为Callable,固有返回值,get()会阻塞 System.out.println("result4:" + futureTask2.get());
// FutureTask实现了RunnableFuture接口,RunnableFuture接口继承了Runnable接口,因此可以作为Thread构造参数传入 new Thread(futureTask2).start(); // 无返回值,get()会阻塞 System.out.println("result5:" + futureTask2.get()); } catch (Exception e) { e.printStackTrace(); } finally { pool.shutdown(); } } static int calcOneToTargetSum(int number) { int sum = 0; for (int i = 0; i < number; i++) { sum += i; } try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } return sum; } }
Runnable和Callable接口的区别:
- Runnable定义的方法是run(),而Callable定义的方法是call()
- Runnable定义的方法是run()无返回值,而Callable定义的方法是call()有返回值
- Runnable定义的方法是run()不能抛出异常,而Callable定义的方法是call()可以抛出异常