Callable和FutureTask分析
解决问题
-
问题
创建线程的2种方式,一种是直接继承Thread,另外一种就是实现Runnable接口,这2种方式都有一个缺陷就是:在执行完任务之后无法获取执行结果。
如果需要获取执行结果,就必须通过共享变量或者使用线程通信的方式来达到效果,这样使用起来就比较麻烦。
-
解决
而自从Java 1.5开始,就提供了Callable和Future,通过它们可以在任务执行完毕之后得到任务执行结果。
优势劣势
- 优势:减少阻塞
应用场景
多线程计算,银行跑批
组成部分
FutureTask
package top.ygy.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Description: TODO(Callable接口)
* @author yangguangyuan
* @date 2019年6月24日
* 实现多线程三种方法:Thread类,实现Runnable接口,实现Callable接口
* 1.区别:
* Runnable接口没有返回值,Callable接口有返回值
* Runnable不会抛异常,Callable回抛出异常
* 实现方法不同,Runnable是run方法,Callable是call方法
*
*/
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//FutureTask(Callable<V> callable)
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
Thread thread = new Thread(futureTask,"AAA");
thread.start();
System.out.println(futureTask.get());
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("************* com in Callable");
return 1024;
}
}
class MyTHread2 implements Runnable{
@Override
public void run() {
}
}
使用
get方法放在最后,不能启动线程就调用get方法,线程的执行需要时间,启动线程后执行其他任务,最后需要结果时,在调用get方法获取结果,做进一步操作,这样可以减少阻塞
一个futureTask启动两次,只调一次线程,多余调用,得出同样的结果,所以不计算
package top.ygy.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;
/**
* @Description: TODO(Callable接口)
* @author yangguangyuan
* @date 2019年6月24日
*
*/
public class CallableDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//FutureTask(Callable<V> callable)
FutureTask<Integer> futureTask = new FutureTask<>(new MyThread());
new Thread(futureTask,"AAA").start();;
new Thread(futureTask,"BBB").start();;
// Integer result02 = futureTask.get();//如果get方法放在其他任务前面,影响其他任务执行,造成阻塞
System.out.println(Thread.currentThread().getName()+"******************");
TimeUnit.SECONDS.sleep(3);
int result01 = 100;
while(!futureTask.isDone()) {
}
Integer result02 = futureTask.get();
System.out.println("result="+(result01+result02));
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println(Thread.currentThread().getName()+"************* com in Callable");
TimeUnit.SECONDS.sleep(3);
return 1024;
}
}
底层原理
-
问题
如何使用Callable?在Thread的构造方法中,有直接构造Runnable接口的构造方法,但是想用Callable,发现没有传入Callable的构造方法,所以需要找到一个可以构造Callable的构造方法
-
解决
- 思想:构造注入,传接口(适配器模式)
- 已知条件
- 有Runnable接口可以进入该方法处理具体问题
- 有Callable接口
- 需要结果:Callable接口可以进入该方法处理具体问题
- 过程
- 类可以实现多个接口,寻找一个类与Runnable接口和Callable接口都有关系的类,就可以解决问题了
FutureTask实现了Runnable接口,构造了Callable接口
相似对比
实现多线程三种方法:Thread类,实现Runnable接口,实现Callable接口
Runnable和Callable区别:
- Runnable接口没有返回值,Callable接口有返回值
- Runnable不会抛异常,Callable回抛出异常
- 实现方法不同,Runnable是run方法,Callable是call方法
参考: