目录
1. Runnable的缺陷
2. Callable接口
3. Future类
1. Runnable的缺陷
- 不能返回一个返回值
- 不能抛出Exception
1.1 为什么会存在这样的缺陷?
通过上图的可以查看Runnable接口,run()方法的返回值是void,且未声明为抛出任何已检查的异常,而我们实现并重写这个方法,自然也不能返回值,也不能抛出异常,因为在对应的Interface/Super class没有声明它
1.2 针对缺陷的补救措施
- 使用Callable,源码中声明了抛出异常
- 用Future来获得子线程的运行结果
2. Callable接口
- Callable是类似于Runnable的接口,实现Callable接口的实现类和实现Runnable的类都是可被其它线程执行的任务
- 实现Callable接口,就要实现call方法,这个方法的返回值是Object,所以返回的结果可以放在Object对象中,所以可以利用Callable的call方法的返回值来获取其他线程的执行结果
使用: 实现Callable接口,同时重写call方法,同时通过Future获取执行的返回值
演示: 线程池的submit方法返回Future对象
public class OneFuture {
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(10);
//Future拿到执行的结果
Future<Integer> future = service.submit(new CallableTask());
try {
//Future具有阻塞性在于其get()方法具有阻塞性
System.out.println(future.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
private static class CallableTask implements Callable<Integer> {
//callable执行任务
@Override
public Integer call() throws Exception {
Thread.sleep(3000);
return new Random().nextInt();
}
}
}
3. Future类
3.1 Future的作用
一个方法的计算过程可能非常耗时,一直在原地等待方法返回,显然不明智。可以把该计算过程放到子线程去执行,并通过Future去控制方法的计算过程,在计算出结果后直接获取该结果
3.2 Callable和Future的关系
-
用
future.get
来获取Callable接口返回的执行结果,还可以通过future.isDone()
来判断任务是否已经执行完了,以及取消这个任务,限时获取任务的结果等。而如果任务没有执行完,future.get()会阻塞调用的线程直到任务执行完后返回结果 -
在
call()
未执行完毕之前,调用get()的线程(假定此时是主线程)会被block,也就是进入到block状态,直到call()
方法返回了结果后,此时future.get()
才会得到该结果,然后主线程才会从block状态切换到runnable状态 -
所以Future是一个存储器,它存储了
call()
这个任务的结果,而这个任务的执行时间是无法提前确定的,因为这完全取决于call()
方法执行的情况
3.3 Future方法介绍
- get():获取结果
- get(long timeout,TimeUnit unit):有超时的获取结果
- cancel():取消任务的执行
- isDone():判断线程是否执行完毕
- isCancelled():判断是否被取消
演示:默认广告的超时和取消
/**
* 描述: 演示get的超时方法,需要注意超时后处理,调用future.cancel()
* 演示cancel传入true和false的区别,代表是否中断正在执行的任务
*/
public class Timeout {
private static final Ad DEFAULT_AD = new Ad("无网络时候的默认广告");
private static final ExecutorService exec = Executors.newFixedThreadPool(10);
static class Ad{
String name;
public Ad(String name) {
this.name = name;
}
@Override
public String toString() {
return "Ad{" +
"name='" + name + '\'' +
'}';
}
}
static class FetchAdTask implements Callable<Ad> {
@Override
public Ad call() throws Exception {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("sleep期间被中断了");
return new Ad("被中断时候的默认广告");
}
return new Ad("轮游订票哪家强?找某程");
}
}
public void printAd(){
Future<Ad> f = exec.submit(new FetchAdTask());
Ad ad;
try {
ad = f.get(2000, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
ad = new Ad("被中断时候的默认广告");
} catch (ExecutionException e) {
ad = new Ad("异常时候的默认广告");
} catch (TimeoutException e) {
ad = new Ad("超时时候的默认广告");
System.out.println("超时,未获取到广告");
boolean cancel = f.cancel(true);
System.out.println("cancel的结果:" + cancel);
}
exec.shutdown();
System.out.println(ad);
}
public static void main(String[] args) {
Timeout timeout = new Timeout();
timeout.printAd();
}
}
演示: FutureTask用法
FutureTask是Future的实现类
/**
* 描述: 演示FutureTask的用法
*/
public class FutureTaskDeom {
public static void main(String[] args) {
Task task = new Task();
FutureTask<Integer> integerFutureTask = new FutureTask<>(task);
// new Thread(integerFutureTask).start();
ExecutorService service = Executors.newCachedThreadPool();
service.submit(integerFutureTask);
try {
System.out.println("task运行结果:" + integerFutureTask.get() );
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private static class Task implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("子线程正在计算");
Thread.sleep(3000);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
}
}
}
3.4 Future注意点
- 当for循环批量获取future的结果时,容易block,get方法调用时应使用timeout限制
- Future和Callable的生命周期不能后退