但是但我们使用get()方法获取异步执行的结果的时候,方法可能会阻塞
我们要通过isDone()判断异步执行结果是否已经完成
使用Future获得异步执行的结果有两个方法:
1. 阻塞的方法: get()
2 轮询: isDone()
都不是很好,我们期望在异步任务执行完毕的时候,我们能自动获得结果.
所以JDK提供了CompletableFuture接口:
任务结束的时候会自动调用回调函数,当发生异常的时候,会调用另一个回调函数.
当异步执行结果执行完毕的时候,我们用.thenAccept()就会正常运行获得异步结果.
我们用exceptionally可以获得异常运行时候的结果
CompletableFuture的优点:
1. 异步任务结束的时候,会自动回调某个对象的方法
2. 异步任务出错的时候,会自动回调某个对象的方法
3. 主线程设置好回调后,不再关心异步任务的执行
CompletableFuture的用法:
我们用thenAccept()来获得一个结果的操作,我们用exceptionally()来获取操作异常时候的操作
package com.leon.day05;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
class DownloadUtil {
public static String download(String url) throws IOException{
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
conn.setDoOutput(false);
conn.setAllowUserInteraction(false);
conn.setConnectTimeout(3000);
conn.setReadTimeout(3000);
conn.connect();
ByteArrayOutputStream output = new ByteArrayOutputStream(10240);
try(InputStream input = conn.getInputStream()){
int n;
byte[] buffer = new byte[2048];
while((n = input.read(buffer)) != -1){
output.write(buffer, 0, n);
}
}
conn.disconnect();
return new String(output.toByteArray(), "UTF-8");
}
}
class StockSupplier implements Supplier<Float>{
@Override
public Float get(){
// 我们传入一个URL,获取股票的价格
String url = "http://hq.sinajs.cn/list=sh000001";
System.out.println("GET: " + url);
try {
String result = DownloadUtil.download(url);
String[] ss = result.split(",");
// 然后我们返回这个价格
return Float.parseFloat(ss[3]);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
public class CompletableFutureSample{
public static void main(String[] args) throws Exception{
// 传入任务,创建一个CompeltableFuture的实例
CompletableFuture<Float> getStockFuture = CompletableFuture.supplyAsync(new StockSupplier());
// 然后通过实例调用thenAccept(),在异步结果正常的情况下打印出股票的价格
getStockFuture.thenAccept(new Consumer<Float>() {
@Override
public void accept(Float price){
System.out.println("Current price: " + price);
}
});
// 调用exceptionally在异步结果出错的时候打印一个error
getStockFuture.exceptionally(new Function<Throwable, Float>() {
@Override
public Float apply(Throwable t){
System.out.println("Error: " + t.getMessage());
return Float.NaN;
}
});
getStockFuture.join();
}
}
多个CompletableFuture可以串行执行:
我们可以先通过异步任务查询证券代码,然后通过查询到的证券代码再启动一个异步任务,最后我们显示一个
证券的价格
package com.leon.day05;
import java.net.URLEncoder;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
class StockLookupSupplier implements Supplier<String>{
String name;
public StockLookupSupplier(String name){
this.name = name;
}
public String get(){
System.out.println("lookup: " + name);
try{
String url = "http://suggest3.sinajs.cn/suggest/type=11,12&key=" + URLEncoder.encode(name, "UTF-8");
String result = DownloadUtil.download(url);
String[] ss = result.split(",");
return ss[3];
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
public class CompletableFutureSequenceSample {
/**
* 两个CompletableFuture串行执行
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception{
String name = "上证指数";
// 通过证券名称获取证券代码
// 首先我们查询了上证指数的代码,得到了证券代码
CompletableFuture<String> getStockCodeFuture = CompletableFuture.supplyAsync(new StockLookupSupplier(name));
// 当获得证券代码后再获得证券价格
// 然后我们根据证券代码查询证券的价格
CompletableFuture<Price> getStockPriceFuture = getStockCodeFuture.thenApplyAsync(new Function<String, Price>() {
public Price apply(String code){
System.out.println("got code: " + code);
try{
String url = "http://hq.sinajs.cn/list=" + code;
String result = DownloadUtil.download(url);
String[] ss = result.split(",");
return new Price(code, Float.parseFloat(ss[3]));
}catch(Exception e){
throw new RuntimeException(e);
}
}
});
// 调用thenAccept打印最后一个结果
getStockPriceFuture.thenAccept(new Consumer<Price>() {
public void accept(Price p){
System.out.println(p.code + ":" + p.price);
}
});
getStockPriceFuture.join();
}
}
我们可以从新浪查询证券价格,也可以从网易查询证券价格.
这两个异步操作可以异步执行,我们可以设置anyof任意一个异步任务
package com.leon.day05;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Supplier;
class StockPrice{
final float price;
final String from;
public StockPrice(float price, String from) {
this.price = price;
this.from = from;
}
public String toString(){
return "Price: " + price + " from " + from;
}
}
/**
* 从新浪获取证券的价格
* @author Leon.Sun
*
*/
class StockFromSina implements Supplier<StockPrice>{
@Override
public StockPrice get(){
String url = "http://hq.sinajs.cn/list=sh000001";
System.out.println("GET: " + url);
try{
String result = DownloadUtil.download(url);
String[] ss = result.split(",");
return new StockPrice(Float.parseFloat(ss[3]), "sina");
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
/**
* 从网易获取证券的价格
* @author Leon.Sun
*
*/
class StockFromNetease implements Supplier<StockPrice>{
@Override
public StockPrice get(){
String url = "http://api.money.126.net/data/feed/0000001,money.api";
System.out.println("GET: " + url);
try{
String result = DownloadUtil.download(url);
int priceIndex = result.indexOf("\"price\"");
int start = result.indexOf(":", priceIndex);
int end = result.indexOf(",", priceIndex);
return new StockPrice(Float.parseFloat(result.substring(start+1, end)), "netease");
}catch(Exception e){
throw new RuntimeException(e);
}
}
}
public class CompletableFutureAnyOfSample {
public static void main(String[] args) throws Exception{
// 我们在main方法中创建两个CompletableFuture对象,分别从新浪和网易获取同一个证券的价格
CompletableFuture<StockPrice> getStockFromSina = CompletableFuture.supplyAsync(new StockFromSina());
CompletableFuture<StockPrice> getStockFromNetease = CompletableFuture.supplyAsync(new StockFromNetease());
// 并行的从新浪和网易获取证券的价格
// 同时通过CompletableFuture.anyOf把两个CompletableFuture对象
CompletableFuture<Object> getStock = CompletableFuture.anyOf(getStockFromSina, getStockFromNetease);
// 当两个结果都返回的时候才执行,这时CompletableFuture的泛型参数是Void
// CompletableFuture<Void> getStock = CompletableFuture.allOf(getStockFromSina, getStockFromNetease);
// thenAccept会在两个中的任意一个完成的时候调用,这样我们打印的是先返回的证券代码
getStock.thenAccept(new Consumer<Object>() {
public void accept(Object result){
System.out.println("Result: " + result);
}
});
getStock.join();
}
}
// 输出结果有时候是新浪先返回,有时候是网易先返回
ComletableFuture对象可以指定异步处理流程:
1. thenAccept()处理正常结果
2. exceptional()处理异常结果
3. thenApplyAsync()用于串行化另一个CompletableFuture
4. anyOf/AllOf用于并行化两个CompletableFuture