创建线程的两种方式:继承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()可以抛出异常