在Java中,Runnable、Callable都表示能被线程执行的任务。
Runnable有以下缺陷:
1、没有返回值
2、不能抛出checked exception
Callable接口类似于Runnable,但能返回一个结果,也可以抛出Exception,使用者需要实现call()方法。
/**
* Callable接口源码
*/
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Future是一个存储器,存储了Callable的返回结果。
ExecutorService提交任务的部分方法如下
void execute(Runnable command); execute方法只能接收Runnable任务,没有返回值。
<T> Future<T> submit(Callable<T> task); submit方法接收Callable任务,返回结果保存在Future中。
Future<?> submit(Runnable task); submit方法也可接收Runnable任务,返回结果保存在Future中。由于Runnable没有返回值,Future中保存的结果是null。
Future的方法
boolean cancel(boolean mayInterruptIfRunning) 尝试取消此任务的执行。
V get() 获取任务结果
V get(long timeout, TimeUnit unit) 限时获取任务结果
boolean isCancelled() 取消任务
boolean isDone() 如果任务完成,返回true。
get()方法获取结果有5种情况
1、任务正常完成,get()方法会立刻返回结果。
2、任务尚未完成(任务未开始或进行中),get()将阻塞并直到任务完成。
3、任务执行过程中抛出Exception,get()方法会抛出ExecutionException;不论call()执行时抛出什么异常,最后get()方法抛出的异常类型都是ExecutionException。
4、任务被取消,get方法会抛出CancellationException。
5、任务超时,get方法有一个重载方法,可以传入延迟时间,如果时间到了还没有获得结果,get方法会抛出TimeoutException。
class Future0{
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
// 线程池execute方法执行Runnable任务
executorService.execute(new Task());
// 线程池submit方法执行Runnable任务
Future f = executorService.submit(new Task());
try {
Object data = f.get();
// 使用submit(Runnable task); future.get()返回null
System.out.println("submit(Runnable task)得到的future.get() "+data);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
/**
* 线程池submit方法执行Callable任务
* 执行submit方法时,线程池立刻返回一个空的Future容器,当线程的任务一旦执行完毕,线程池会把结果填入Future中
*/
Future<Integer> future = executorService.submit(new CallableTask());
try {
/**
* 任务正常完成,get()方法会立刻返回结果。
* 任务尚未完成(任务未开始或进行中),get()将阻塞并直到任务完成。
*/
Integer data = future.get();
System.out.println("submit(Callable<T> task)得到的future.get() "+data);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
executorService.shutdown();
}
static class Task implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"run()运行");
}
}
static class CallableTask implements Callable<Integer>{
@Override
public Integer call() throws Exception {
TimeUnit.SECONDS.sleep(2);
return new Random().nextInt(10);
}
}
}
/**
* Callable抛出异常时,主线程不会抛异常,直到执行future.get()的时候才会抛异常
* 不论call()执行抛出什么异常,最后get()方法抛出的异常类型都是ExecutionException。
*/
class Future01{
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(10);
Future<Object> future = executorService.submit(() -> {
throw new IllegalArgumentException("在Callable中抛出IllegalArgumentException异常");
});
TimeUnit.SECONDS.sleep(1);
System.out.println("任务是否全部运行完毕。"+future.isDone());
try {
Object o = future.get();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("抛出InterruptedException");
} catch (ExecutionException e) {
e.printStackTrace();
// System.out.println(e.getCause().getMessage());
System.out.println("抛出ExecutionException");
}
executorService.shutdown();
}
}
/**
* 超时与取消任务演示
*
* cancel方法,取消任务
* 1、如果任务还没开始执行,任务会被取消,未来也不会被执行,返回true。
* 2、如果任务已完成或已取消,cancel方法会执行失败,返回false。
* 3、如果任务正在执行,根据cancel(boolean mayInterruptIfRunning)参数取消任务,true取消,false不取消。
* cancel(true)适用于能处理interrupt的任务
* cancel(false)适用于。
* 1、不能处理interrupt的任务,或者调用者不确定任务能否处理interrupt。
* 2、需要等待已经开始的任务执行完成。
*/
class Future02{
private static final ExecutorService exec = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
Future<Ad> future = exec.submit(() -> {
//TimeUnit.MILLISECONDS.sleep(1000);
// 任务超时演示
TimeUnit.MILLISECONDS.sleep(3000);
return new Ad("某程的旅游广告");
});
Ad ad;
try {
ad = future.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 = future.cancel(true);
System.out.println("cancel的结果:"+cancel);
}
System.out.println(ad);
exec.shutdown();
}
static class Ad{
String name;
public Ad(String name) {
this.name = name;
}
@Override
public String toString() {
return "Ad{" +
"name='" + name + '\'' +
'}';
}
}
}
还有一个常用的类FutureTask,FutureTask实现了RunnableFuture,RunnableFuture同时继承了Runnable、Future接口,在Java中,接口可以多实现。
FutureTask是一种包装器,其中的一个构造函数是接受Callable类型的参数,既可以作为Callable被线程执行,又可以作为Future得到Callable的返回结果。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
FutureTask类图如下
class FutureTask1{
static ExecutorService service = Executors.newCachedThreadPool();
public static void main(String[] args) {
// 创建Callable对象
Callable callable = () -> {
System.out.println("子线程正在计算");
TimeUnit.SECONDS.sleep(1);
int sum = 0;
for (int i = 0; i < 100; i++) {
sum += i;
}
return sum;
};
// 使用Callable创建FutureTask
FutureTask<Integer> futureTask = new FutureTask<Integer>(callable);
// 线程池提交FutureTask
service.submit(futureTask);
try {
System.out.println("FutureTask运行结果"+futureTask.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
service.shutdown();
}
}