1.Future设计模式:
1).定义了一个接口Data,这是客户端希望获取的数据,这个接口有两个实现分别是RealData和FutureData,RealData是真实的数据,FutureData是用来在真实数据构建完后获取它的。
2).在提出一个请求后,会立即返回一个FutureData类型的数据futureData,这里注意此时futureData中并没有真实值,此时如果使用futureData.getResult()获取数据,此时线程会阻塞。而Future模式期待的并不是这种情况,而是在发出请求后,线程去做别的事务,在经历了一段时间后,再从futureData中提取出真实数据 。
3)正常的流程如下:
RealData和FutureData都实现了Data接口
主线程发出请求后,立即得到一个futureData数据,同时主线程开启一个新的线程
主线程去做别的事务—此时futureData数据并不能被使用,如果此时要理解获取futureData中的数据将等待
新的线程构造真实数据RealData,在RealData的构造函数中将获取请求的结果,并保存在RealData的私有属性中,然后调用FutureData的setRealData(RealData rd)方法,返回获取请求的结果,并唤醒可能阻塞的方法getResult()
主线程使用到futureData数据,futureData使用getResult()方法获取真实数据
注意点:
RealData和FutureData都实现了Data接口
RealData构建真实结果
FutureData只有RealData的引用,同时在FutureData中获取getResult()方法,使用了等待唤醒的方式,在数据没有准备好的时候,获取真实数据将进入等待。
interface Data {
public String getResult();
}
//实现了一个快速返回的RealData包装
class FutureData implements Data {
protected RealData realData = null; //成员变量就是真实数据ReadData
protected boolean isReady = false; //数据准备好后设置为true
public synchronized void setRealData(RealData realData) { //等待RealData准备了
if (isReady) {
return;
}
this.realData = realData; //获取真实的数据
isReady = true;
notify(); //RealData已经被注入,通知getResult
}
@Override
public synchronized String getResult() { //如果数据没有准备好,会进入阻塞
while (!isReady) {
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return realData.result; //获取了处理后的数据
}
}
class RealData implements Data{
protected final String result;
public RealData(String param) {
//RealData的构造很慢,需要用户等待很久,这里用sleep模拟
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i < 10; i++) {
stringBuffer.append(param);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
result = stringBuffer.toString(); //处理后的数据
}
public String getResult() {
return result;
}
}
class Client {
public Data request(final String queryStr) {
final FutureData futureData = new FutureData();
//开启了一个新的线程
new Thread() {
public void run() {//RealData的构造很慢,所以在单独的线程中运行
RealData realData = new RealData(queryStr); //耗时的操作
futureData.setRealData(realData);
}
}.start();
return futureData; //这是一个假数据
}
}
public class TestFuture {
public static void main(String args[]) {
Client client = new Client();
//这里会立即返回,因为得到的是FutureData而不是RealData
Data data = client.request("name");
System.out.println("请求完毕");
System.out.println("返回一个假数据: "+data);
System.out.println("去处理别的事情");
try {
//这里用一个sleep代替了对其他业务逻辑的处理
//在处理这些业务逻辑的过程中,RealData被创建,从而充分利用了等待时间
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//使用真实的数据
System.out.println("数据=" + data.getResult());
}
}
说明
:
Future中有一个真实数据RealData,有一假数据FutureData,这两者的关系,1)它们都实现了Data接口 2)FutureData中持有RealData的引用,通过这个引用获取真实数据,FutureData中使用的等待/唤醒模式,如果获取数据的时候数据没有准备好(真实数据RealData的构建较花时间),就阻塞获取的线程。
2.FutureTask学习
如果开启的新线程执行完成后,要返回结果,这时候就可以考虑使用实现Callable接口而不是Runnable接口。最常见的场景是如果开启一个新线程去执行计算(因为计算可能花去较多时间),计算的结果也许不是立即使用,这时候可以使用future模式。
当FutureTask处于未启动状态时,执行FutureTask.get()方法将导致调用线程阻塞。
当FutureTask处于已完成状态时,执行FutureTask.get()方法将导致线程立即返回结果。
FutureTask常见使用示例代码:
class CallableDemo implements Callable<Integer> {//泛型,Integer是线程执行返回结果
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算啦!");
Thread.sleep(2000);
for(int i=0 ;i<5000;i++){
sum=sum+i;
}
System.out.println("Callable子线程计算结束!");
return sum;
}
}
public class TestFuture{
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = Executors.newFixedThreadPool(1);
FutureTask<Integer> task = new FutureTask<>(new CallableDemo());
es.submit(task); //FutureTask实现了Runnble接口和Future接口
es.shutdown();
System.out.println("主线程继续执行别的任务");
System.out.println("计算执行结果:"+task.get());
}
}
Future常见使用示例代码:
class CallableDemo implements Callable<Integer> {//泛型,Integer是线程执行返回结果
private int sum;
@Override
public Integer call() throws Exception {
System.out.println("Callable子线程开始计算啦!");
Thread.sleep(2000);
for(int i=0 ;i<5000;i++){
sum=sum+i;
}
System.out.println("Callable子线程计算结束!");
return sum;
}
}
public class TestFuture{
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService es = Executors.newFixedThreadPool(1);
Future<Integer> future = es.submit(new CallableDemo());
System.out.println("主线程继续执行别的任务");
System.out.println("计算执行结果:"+future.get());
}
}
参考博客:
https://www.cnblogs.com/dolphin0520/p/3949310.html
https://blog.csdn.net/javazejian/article/details/50896505
小结:
1)使用Callable接口创建线程可以获取返回值
2)FutureTask实现了Runnable接口和Future接口,因为FutureTask实现了Runnable接口,所以FutureTask可以交给Executor执行
3)Future接口中常见的方法是get()方法,用来获取Callable接口的返回值
4)应用场景:我们需要计算一个数,而这个数的计算很耗时,所以我们可以创建一个新线程去执行计算,而主线程继续做其他事情,而后面用到这个数据的时候,我们使用Future或者FutureTask来获取就可以了。
5)FutureTask有3中状态:
未启动,已启动,已完成 只有已完成状态下使用get()方法才返回结果,未启动和已启动状态调用get()方法会阻塞。