前言
当被Netty吊打时,又回来继续攀知识树
Callable
Callable很简单,和Runnable类似,都是用于启动线程的接口
区别在与:
- Runnable没有返回值;Callable可以返回执行结果,是个泛型< V>,和Future、FutureTask配合可以用来获取异步执行的结果(当然Runnable也可以异步执行)
- Callable接口的call()方法允许抛出异常;Runnable的run()方法异常只能在内部消化,不能往上继续抛
- Callalbe接口支持返回执行结果,需要调用FutureTask.get()得到,此方法会阻塞主进程的继续往下执行,如果不调用不会阻塞
使用方法:implements Callable接口,然后重写call()方法即可,call()方法返回Object对象(即任何类型都可返回)
public class Task implements Callable {
private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
@Override
public Object call() throws Exception {
System.out.println("任务运行中。。。时间:"+sdf.format(new Date()));
Thread.sleep(8000);
int num = 100;
return num;
}
}
完成一个异步案例
package com.company.javaBasis.Future;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.*;
public class Task implements Callable {
private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
@Override
public Integer call() throws Exception {
System.out.println("任务运行中。。。时间:"+sdf.format(new Date()));
Thread.sleep(8000);
int num = 100;
return num;
}
}
class Test{
private static SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
public static void main(String[] args) {
//创建线程池
ExecutorService executorService = Executors.newCachedThreadPool();
Task task = new Task();
Future future = executorService.submit(task);
//关闭线程池,还在运行的任务继续运行
executorService.shutdown();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程运行中。。。时间:"+sdf.format(new Date()));
try {
System.out.println("Task 运行结果:"+future.get()+",时间:"+sdf.format(new Date()));
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("所有任务运行完毕。。。时间:"+sdf.format(new Date()));
}
}
我们程序的运行应该是:
Task任务(阻塞8秒) - 》 阻塞5秒 - 》 输出Task结果
根据输出的时间可以看出,在Task阻塞8秒的同时,主线程阻塞5秒也在进行中,所以一共阻塞了8秒,这就是异步操作
future.get()也阻塞了主线程,让主线程等待Task结果
Future
从JDK1.5开始,提供了Callable和Future,通过它们可以异步的在任务执行完毕之后得到任务执行结果
同步:运行时必须等待上一步执行完成,才能进行到下一步
异步:运行时不必等待上一步执行完成,立刻执行下一步
网页常用的AJAX就是个很典型的异步技术
Future是一个未来结果,可以对线程任务的执行结果进行取消、查询是否完成、获取结果
即可以先得到Future对象,然后随着线程任务的执行,将结果赋给Future对象
Future定义了5个方法:
- cancel方法:试图取消任务的执行
取消成功返回true,失败返回false
mayInterruptIfRunning参数表示是否允许取消正在执行却没有执行完毕的任务 - get()方法
获得任务结果,这个方法会将线程阻塞 - get(long timeout,TimeUnit unit)
获得任务结果,设置阻塞时间
例如上面的例子设置
future.get(1000,TimeUnit.MILLISECONDS)
只在这里等待一秒结果,明显是等不到结果了,因为Task要阻塞8秒
- isCancelled方法
表示任务是否被取消成功,如果在任务正常完成前被取消成功,返回 true - isDone方法
表示任务是否已经完成,若任务完成,则返回true
从这五个方法中可以看出Future提供3种功能:判断任务是否完成;中断任务;获得任务结果
(功能比较弱,所以Netty中改进了Future,增加了很多功能)
FutureTask
FutureTask是Future的实现类
我们可以Debug看看:
FutureTask定义了这些方法
FutureTask是一个可以取消的异步结果,提供了 Future的实现
FutureTask实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口。所以它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值
所以前面的案例如果用FutureTask实现,可以省去线程池或者将FutureTask注册进线程池
总结
- Callable可以作为一种线程的接口,和Runnable不同的是,Callable有返回值,且可以抛出异常
- Future是未来结果,对于线程,可以立即得到Future对象,然后等线程执行完了,再从Future对象中得到结果,可以通过Future.get()方法阻塞获得结果
- FutureTask是Future的实现类,即能作为一个线程,也可以作为Future