大话Android多线程(四) Callable、Future和FutureTask
版权声明:本文为博主原创文章,未经博主允许不得转载 源码:github.com/AnliaLee 大家要是看到有错误的地方或者有啥好的建议,欢迎留言评论
前言
实现Callable接口创建线程
我们先简单举个栗子,看看通过实现Callable接口来创建线程的方式和之前两种有什么区别
某日,高铁站前,老C和他儿子道别,儿子:“爸爸,你走吧。”老C望了望路边的小摊,说道

说完老C便走去小摊买橘子了(实现Callable接口,重写call()方法)
public static class TestCallable implements Callable{
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + ":我买几个橘子去。你就在此地,不要走动" + " 时间:" + getTime());
Thread.sleep(2000);//模拟买橘子的时间
return Thread.currentThread().getName() + ":我买完橘子回来了" + " 时间:" + getTime();
}
}
儿子自然是乖乖站在原地等爸爸买橘子
public class CallableTest {
//省略部分代码...
public static void main(String args[]){
TestCallable callable = new TestCallable();
FutureTask<String> futureTask = new FutureTask<String>(callable);
Thread thread1 = new Thread(futureTask, "爸爸");
thread1.start();
System.out.println("儿子还没收到橘子" + " 时间:" + getTime());//验证主线程的执行情况
try{
System.out.println(futureTask.get());
System.out.println("儿子收到橘子" + " 时间:" + getTime());//验证主线程的执行情况
}catch (InterruptedException | ExecutionException e){
}
}
}
正常买到橘子的运行结果如下

如果没买到橘子呢(我们尝试在call()方法中抛出异常,然后在调用get()方法时进行捕获)?

public static class TestCallable implements Callable{
private int ticket = 10;
@Override
public String call() throws Exception {
System.out.println(Thread.currentThread().getName() + ":我买几个橘子去。你就在此地,不要走动" + " 时间:" + getTime());
Thread.sleep(2000);//模拟买橘子的时间
System.out.println(Thread.currentThread().getName() + ":橘子卖完了" + " 时间:" + getTime());
throw new NullPointerException("橘子卖完了");
}
}
public static void main(String args[]){
TestCallable callable = new TestCallable();
FutureTask<String> futureTask = new FutureTask<String>(callable);
Thread thread1 = new Thread(futureTask, "爸爸");
thread1.start();
System.out.println("儿子站在原地" + " 时间:" + getTime());//验证主线程的执行情况
try{
System.out.println(futureTask.get());
System.out.println("儿子收到橘子" + " 时间:" + getTime());//验证主线程的执行情况
}catch (InterruptedException | ExecutionException e){
System.out.println("儿子没收到橘子" + " 时间:" + getTime());//验证主线程的执行情况
}
}

另外需要注意的是,爸爸的钱只够买一袋橘子(任务只能执行一次)
public static void main(String args[]){
TestCallable callable = new TestCallable();
FutureTask<String> futureTask = new FutureTask<String>(callable);
Thread thread1 = new Thread(futureTask, "爸爸去了第一个摊位");
Thread thread2 = new Thread(futureTask, "爸爸去了第二个摊位");
Thread thread3 = new Thread(futureTask, "爸爸去了第三个摊位");
thread1.start();
thread2.start();
thread3.start();
System.out.println("儿子站在原地" + " 时间:" + getTime());//验证主线程的执行情况
try{
System.out.println(futureTask.get());
System.out.println("儿子收到橘子" + " 时间:" + getTime());//验证主线程的执行情况
}catch (InterruptedException | ExecutionException e){
System.out.println("儿子没收到橘子" + " 时间:" + getTime());//验证主线程的执行情况
}
}

总结上面的案例:
- Callable在被线程执行后,可以提供一个返回值,我们可以通过Future的get()方法拿到这个值
Future是一个接口,而FutureTask实现了RunnableFuture接口,RunnableFuture继承了Runnable接口和Future接口(继承关系见下图)

FutureTask用于异步获取执行结果或取消执行任务的场景,它的主要功能有:
- 可以判断任务是否完成
- 可以获取任务执行结果
- 可以中断任务
更详细的源码解析及用法可以看下这几篇博客 Java并发编程:Callable、Future和FutureTask原理解析 Java FutureTask 源码分析 Android上的实现 FutureTask的用法及两种常用的使用场景
- Callable的call()方法可以抛出异常,我们可以在尝试执行get()方法时捕获这个异常
区别于实现Runnable接口创建线程的方式,以上这两点功能Runnable就无法实现了
- FutureTask可以确保任务只执行一次
- 我们在某条线程执行get()方法时,该线程会被阻塞,直到Future拿到Callable.call()方法的返回值
此外,除了直接new一个Thread,我们还可以利用线程池结合Callable执行多线程任务
public static void main(String args[]){
TestCallable callable = new TestCallable();
ExecutorService executor = Executors.newCachedThreadPool();
Future<String> future = executor.submit(callable);
//或者
//FutureTask<String> future = new FutureTask<String>(callable);
//executor.execute(future);
System.out.println("儿子站在原地" + " 时间:" + getTime());//验证主线程的执行情况
try{
System.out.println(future.get());
System.out.println("儿子收到橘子" + " 时间:" + getTime());//验证主线程的执行情况
}catch (InterruptedException | ExecutionException e){
System.out.println("儿子没收到橘子" + " 时间:" + getTime());//验证主线程的执行情况
}
}
那么线程池又是啥?留到下一章我们再“大话”一番吧
本篇博客到此结束,若大家有啥疑问或建议欢迎留言评论,感激不尽。如果觉得写得还不错麻烦点个赞,你们的支持是我最大的动力~
转载自:https://juejin.cn/post/6844903561407496206