Request-Per-Task每个请求一个任务可以实现异步任务,但是不能获取任务的结果,Future模式可以获取异步任务的结果
独立开线程:使用FutureTask或者线程条件(if balk while wait)实现Future模式
一个Data的接口
package com.FutureExample;
public interface Data {
public abstract String getContent();
}
RealData实现Data,构造方法延时产生真正数据,包含获取真正的数据getContent方法
package com.FutureExample;
public class RealData implements Data {
private final String content;
public RealData(int count,char c){
System.out.println("构造产生真实数据需要一段时间:"+count+"个"+c+"开始");
char[] buffer=new char[count];
for(int i=0; i<count;i++){
buffer[i]=c;
try{
Thread.sleep(100);
}catch (InterruptedException e){}
}
System.out.println("构造产生真实数据需要一段时间:"+count+"个"+c+"结束");
this.content=new String(buffer);// 初始化
}
@Override
public String getContent() {
return content;
}
}
FutrueData实现RealData,使用if balk 和while wait模式设置和获得数据
package com.FutureExample;
public class FutureData implements Data {
private RealData realData=null;
private boolean dataReadyFlag=false;// 数据是否准备完成
public synchronized void setRealData(RealData realData){
if(dataReadyFlag){// 数据准备完成直接走开
return;
}
this.realData=realData;
dataReadyFlag=true;
notifyAll();
}
@Override
public synchronized String getContent() {
while (!dataReadyFlag){
try {
wait();
}catch (InterruptedException e){}
}
return realData.getContent();
}
}
使用FutureTask实现的FutureData,不需要考虑细节,只需要传入Callable接口,调用FutureTask的get方法就能获取Callable的返回对象(RealData)
首先考察FutureTask 实现了Runnable接口,可以使用new Thread(FutureTask obj).start() 。调用FutureTask中的run方法,实际上是运行其中的run方法,可以看出来run方法中调用了Callable接口中的call方法,将结果设置成result。
FutureTask中只有有参构造方法,所以子类必须含有参构造方法而且必须显式调用父类构造方法。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
package com.FutureExample;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class FutureDataBetter extends FutureTask<RealData> implements Data {
public FutureDataBetter(Callable<RealData> callable){
// FutureTask调用Callable的call方法的结果设置成result。
super(callable);// 因为先有父类才能有子类,父类只有有参构造方法,子类必须显式的调用,才能构造出父类,(而且只能在子类的有参构造函数中调用,无参的构造函数中默认有一个super()可以调用父类的无惨构造函数,但是有参必须显示调用。)
}
@Override
public String getContent() {
String str=null;
try{
str=super.get().getContent();// super.get()获得的是RealData,realdata构造需要一段时间延时获取
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return str;
}
}
class FutureDataBetter extends FutureTask。FutureDataBetter继承了FutureTask,FutureTask没有无参构造方法,只有有参构造方法,所以在FutureDataBetter中必须同样存在有参构造函数,并在构造函数中显示的调用调用父类的有参构造函数-super(callable)。否则会报在父类构造函数调用前是不能引用Callable对象的&父类中不存在默认的构造函数(无参构造函数)。
Host类:用来发出请求将来的数据(FutureData)
package com.FutureExample;
import java.util.concurrent.Callable;
public class Host {
public Data requstFutrueData(final int count,final char c){
System.out.println("请求futrueData开始"+count+c);
// final FutureData futureData=new FutureData();
//
// new Thread(){
// public void run(){
// RealData realData=new RealData(count, c);
// futureData.setRealData(realData);
// }
// }.start();
FutureDataBetter futureDataBetter=new FutureDataBetter(new Callable<RealData>() {
@Override
public RealData call() throws Exception {
return new RealData(count, c);// new对象需要时间
}
});
// 开线程:调用Callable的call方法的结果设置成result
new Thread(futureDataBetter).start();
System.out.println("请求futrueData结束"+count+c);
// return futureData;
return futureDataBetter;
}
}
使用线程池,阻塞获取get现Future
这种模式通过线程池实现。更简单,省略了Host手动开线程请求数据。但这种方法会阻塞主线程。
get():获取结果(可能会等待)
get(long timeout, TimeUnit unit):获取结果,但只等待指定的时间;
cancel(boolean mayInterruptIfRunning):取消当前任务;
isDone():判断任务是否已完成。
package com.FutureExample;
import java.util.concurrent.Callable;
public class CallableTask implements Callable<String> {
private RealData realData;
public CallableTask(int count,char c){
this.realData=new RealData(count, c);
}
@Override
public String call() throws Exception {
return this.realData.getContent();
}
}
通过源码可以看到 Future futureData= executorService.submit(callableTask);
submit提交了一个Callable任务,这个Callable返回结果的最终实现是FurtureTask调用了run方法进而调用了call方法,把结果set成result。不用开线程实现,线程池实现代替了Host开线程的实现。
ComfortableFuture异步回调
底层也是线程池不过不会阻塞主线程,使用回调的方式执行
异步任务结束时,会自动回调某个对象的方法;
异步任务出错时,会自动回调某个对象的方法;
主线程设置好回调后,不再关心异步任务的执行。
但是主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭
串行调用
public class Main {
public static void main(String[] args) throws Exception {
// 第一个任务:
CompletableFuture<String> cfQuery = CompletableFuture.supplyAsync(() -> {
return queryCode("中国石油");
});
// cfQuery成功后继续执行下一个任务:
CompletableFuture<Double> cfFetch = cfQuery.thenApplyAsync((code) -> {
return fetchPrice(code);
});
// cfFetch成功后打印结果:
cfFetch.thenAccept((result) -> {
System.out.println("price: " + result);
});
// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
Thread.sleep(2000);
}
static String queryCode(String name) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
return "601857";
}
static Double fetchPrice(String code) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
return 5 + Math.random() * 20;
}
}
并行使用
public class Main {
public static void main(String[] args) throws Exception {
// 两个CompletableFuture执行异步查询:
CompletableFuture<String> cfQueryFromSina = CompletableFuture.supplyAsync(() -> {
return queryCode("中国石油", "https://finance.sina.com.cn/code/");
});
CompletableFuture<String> cfQueryFrom163 = CompletableFuture.supplyAsync(() -> {
return queryCode("中国石油", "https://money.163.com/code/");
});
// 用anyOf合并为一个新的CompletableFuture:
CompletableFuture<Object> cfQuery = CompletableFuture.anyOf(cfQueryFromSina, cfQueryFrom163);
// 两个CompletableFuture执行异步查询:
CompletableFuture<Double> cfFetchFromSina = cfQuery.thenApplyAsync((code) -> {
return fetchPrice((String) code, "https://finance.sina.com.cn/price/");
});
CompletableFuture<Double> cfFetchFrom163 = cfQuery.thenApplyAsync((code) -> {
return fetchPrice((String) code, "https://money.163.com/price/");
});
// 用anyOf合并为一个新的CompletableFuture:
CompletableFuture<Object> cfFetch = CompletableFuture.anyOf(cfFetchFromSina, cfFetchFrom163);
// 最终结果:
cfFetch.thenAccept((result) -> {
System.out.println("price: " + result);
});
// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
Thread.sleep(200);
}
static String queryCode(String name, String url) {
System.out.println("query code from " + url + "...");
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
}
return "601857";
}
static Double fetchPrice(String code, String url) {
System.out.println("query price from " + url + "...");
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
}
return 5 + Math.random() * 20;
}
}
anyOf()可以实现“任意个CompletableFuture只要一个成功”,allOf()可以实现“所有CompletableFuture都必须成功”,这些组合操作可以实现非常复杂的异步流程控制。
最后我们注意CompletableFuture的命名规则:
xxx():表示该方法将继续在已有的线程中执行;
xxxAsync():表示将异步在线程池中执行。
thenAccept()处理正常结果;
exceptional()处理异常结果;
thenApplyAsync()用于串行化另一个CompletableFuture;
anyOf()和allOf()用于并行化多个CompletableFuture。
Main方法
package com.FutureExample;
import java.util.concurrent.*;
public class Main {
public static void main(String[] args) throws ExecutionException, InterruptedException, ClassNotFoundException {
System.out.println("主线程开启");
Host host=new Host();
Data data1=host.requstFutrueData(10, 'a');
Data data2=host.requstFutrueData(20, 'b');
Data data3=host.requstFutrueData(30, 'c');
System.out.println("主线程其他任务开启");
// 线程池Future
ExecutorService executorService= Executors.newCachedThreadPool();
CallableTask callableTask=new CallableTask(40, 'd');
Future<String> futureData= executorService.submit(callableTask);
// comfortable future
RealData realData=new RealData(50, 'e');
CompletableFuture completableFuture=CompletableFuture.supplyAsync(realData::getContent);
try {
Thread.sleep(500); // 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
}catch (InterruptedException e){}
System.out.println("data1:"+data1.getContent());
System.out.println("data2:"+data2.getContent());
System.out.println("data3:"+data3.getContent());
System.out.println("futureData:"+futureData.get());
completableFuture.thenAccept((result)->{
System.out.println("comfatable future:"+result);
});
System.out.println("主线程结束");
}
}
结果: 主线程开启
请求futrueData开始10a 请求futrueData结束10a
请求futrueData开始20b 请求futrueData结束20b
请求futrueData开始30c 请求futrueData结束30c
主线程其他任务开启
构造RealData对象需要一段时间:10个a开始 构造RealData对象需要一段时间:30个c开始
构造RealData对象需要一段时间:20个b开始 构造RealData对象需要一段时间:40个d开始
构造RealData对象需要一段时间:10个a结束 构造RealData对象需要一段时间:20个b结束
构造RealData对象需要一段时间:30个c结束 构造RealData对象需要一段时间:40个d结束
构造RealData对象需要一段时间:50个e开始 构造RealData对象需要一段时间:50个e结束
data1:aaaaaaaaaa
data2:bbbbbbbbbbbbbbbbbbbb
data3:cccccccccccccccccccccccccccccc
futureData:dddddddddddddddddddddddddddddddddddddddd
comfatable
future:eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
主线程结束