Callable、Future、FutureTask之间的关系
- Callable是Runnable封装的异步运算任务
- Future接收Callable异步运算结果
- FutureTask封装Future的实现类
Callable与Runnable的区别
- Callable定义的方法是call(),Runnable定义的方法是run()
- Callable可以抛出异常,Runnable不可以抛出异常
- Callable有返回值,Runnable无返回值
Future
public interface Future<V> {
boolean cancel(boolean mayInterruptIfRunning);
boolean isCancelled();
boolean isDone();
V get() throws InterruptedException, ExecutionException;
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
FutureTask
FutureTask是Future的实现类,是可以取消异步计算的,在任务完成时得到计算结果,没完成时使用get()方法阻塞等待结果。
FutureTask不仅实现了Futrue接口,还实现了Runnable接口,也就是说即可以当作一个任务交给Exectutor来进行运算,还可以通过Thread来将任务交给线程。
将任务交给Thread线程来运行
public class Cdemo1 implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("callable is coming");
Thread.sleep(5000);
return new Random().nextInt();
}
}
public class demo1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
Cdemo1 demo=new Cdemo1();
FutureTask future=new FutureTask(demo);
Thread thread=new Thread(future);
thread.start();
Thread.sleep(2000);
//取消任务
/** 如果取消任务,当前取消的任务已完成,则取消失败返回false;当前取消的任务未完成,则取消成
功返回true
根据任务中代码在取消任务时,还在休眠的状态
*/
future.cancel(true);
//判断是否在任务正常完成前取消
/**判断任务是否取消,如果在任务完成前取消了,那就是返回true
*/
System.out.println("future is cancel:" + future.isCancelled());
if(!future.isCancelled()){
System.out.println("futrue is cancelled");
}
/** 判断任务是否完成,完成就返回false,完成的情况有:正常完成终结,异常以及取消任务完成
*/
System.out.println("future is done:" + future.isDone());
if(!future.isDone()){
System.out.println("接收的结果:"+future.get());
}else {
System.out.println("future is done!");
}
}
}
将任务放入到线程池中
public class FutureCallable implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("callable is coming:"+System.currentTimeMillis());
Thread.sleep(10000);
return "任务已执行完毕";
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<String> callable=new FutureCallable();
ExecutorService service= Executors.newSingleThreadExecutor();
Future<String> future=service.submit(callable);
//shutdown执行完成后,关闭线程池,只是不接受新的任务,但是会将已有的任务执行完毕
service.shutdown();
Thread.sleep(5000);
System.out.println("休眠5s:"+System.currentTimeMillis());
String str= future.get();
System.out.println("future已拿到时间"+System.currentTimeMillis()+"内容:"+str);
}
}
基本相差10s,是因为在线程中任务还未执行完成时,Future中的get()方法会阻塞主线程,一直等得任务完成后,才接收结果并继续执行。
使用线程池的情况下当程序结束时记得调用shutdown关闭线程池
如果不调用shutdown()方法来关闭线程池,那么在主线程执行结束后,JVM仍存在。因为只要有用户线程,JVM就是不退出的,线程池中ThreadFactory创建的都是用户线程(用户线程就是独立于操作系统的线程,不依赖于操作系统的核心,main就是用户线程), 通过Thread.setDaemon(false)可以设置为用户线程,ThreadFactory中创建的核心线程是一直存在的,当没有任务之后,核心线程空闲,就会被阻塞,那么JVM就会一直存在。
使用完线程池不调用shutdown方法会导致线程池的线程资源一直不被释放。
线程池使用FutureTask时需要注意的事情
线程池使用FutureTask时,如果拒绝策略使用了DiscardPolicy(放弃任务)和DiscardOldPolicy(放弃队列中最早的任务,用新的进入的任务替代),被拒绝的任务的Future对象上调用了get()方法,那么可能线程会一直陷入阻塞状态。
以DiscardPolicy为例,在使用拒绝策略DiscardPolicy时,并没有设置Future的状态,那么状态就是等于0,为NEW,而且一直为NEW,在调用get()方法时,会比较Futrue的状态,如果大于等于COMPLETING(1)时,就会阻塞,只有状态值等于NORMAL时才会正常返回。
所以在实际开发过程中,尽量使用带超时的get()方法,以避免线程的阻塞。