Skip to content

Commit a1ed655

Browse files
committed
[add] add CompletableFuture examples in Java 8 module.
1 parent dae9e1f commit a1ed655

File tree

14 files changed

+662
-0
lines changed

14 files changed

+662
-0
lines changed

java8/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,20 @@ Optional
6464
- OptionalMain: ptional 用法(map,flatMap,filter)
6565
- OptionalInAction: 使用 Optional 实战示例
6666
- ReadPositiveIntParam: 使用 Optional 示例
67+
68+
CompletableFuture
69+
70+
- v1/Shop: 商店类第一版,没有折扣
71+
- v1/ShopMain: 使用异步 API 的使用示例
72+
- v1/BestPriceFinder: 通过不同的方案实现价格查询:顺序流,并行流,CompletableFuture
73+
- v1/BestPriceFinderMain: 测试每种实现方案的执行时间
74+
75+
- AsyncShop: 使用工厂方法 supplyAsync 创建 CompletableFuture
76+
- AsyncShopClient: 演示错误处理
77+
- ExchangeService: 货币兑换类
78+
- Discount: 折扣类
79+
- Quote: 解析商店返回的字符串
80+
- Shop: 商店类,报价按照 `name:price:code` 的格式
81+
- BestPriceFinder: 添加折扣后的版本
82+
- BestPriceFinderMain: 测试每种实现方案的执行时间
83+
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package com.brianway.learning.java8.effective.async;
2+
3+
import static com.brianway.learning.java8.effective.async.Util.delay;
4+
import static com.brianway.learning.java8.effective.async.Util.format;
5+
6+
import java.util.Random;
7+
import java.util.concurrent.CompletableFuture;
8+
import java.util.concurrent.Future;
9+
10+
/**
11+
* 使用工厂方法 supplyAsync 创建 CompletableFuture
12+
*/
13+
public class AsyncShop {
14+
15+
private final String name;
16+
private final Random random;
17+
18+
public AsyncShop(String name) {
19+
this.name = name;
20+
random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2));
21+
}
22+
23+
public Future<Double> getPrice(String product) {
24+
/* // 抛出 CompletableFuture 内的异常
25+
CompletableFuture<Double> futurePrice = new CompletableFuture<>();
26+
new Thread( () -> {
27+
try {
28+
double price = calculatePrice(product);
29+
futurePrice.complete(price);
30+
} catch (Exception ex) {
31+
futurePrice.completeExceptionally(ex);
32+
}
33+
}).start();
34+
return futurePrice;
35+
*/
36+
// 使用工厂方法
37+
return CompletableFuture.supplyAsync(() -> calculatePrice(product));
38+
}
39+
40+
private double calculatePrice(String product) {
41+
delay();
42+
if (true) {
43+
throw new RuntimeException("product not available");
44+
}
45+
return format(random.nextDouble() * product.charAt(0) + product.charAt(1));
46+
}
47+
48+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.brianway.learning.java8.effective.async;
2+
3+
import java.util.concurrent.Future;
4+
5+
/**
6+
* 演示错误处理
7+
*/
8+
public class AsyncShopClient {
9+
10+
public static void main(String[] args) {
11+
AsyncShop shop = new AsyncShop("BestShop");
12+
long start = System.nanoTime();
13+
Future<Double> futurePrice = shop.getPrice("myPhone");
14+
long incocationTime = ((System.nanoTime() - start) / 1_000_000);
15+
System.out.println("Invocation returned after " + incocationTime + " msecs");
16+
try {
17+
System.out.println("Price is " + futurePrice.get());
18+
} catch (Exception e) {
19+
throw new RuntimeException(e);
20+
}
21+
long retrivalTime = ((System.nanoTime() - start) / 1_000_000);
22+
System.out.println("Price returned after " + retrivalTime + " msecs");
23+
}
24+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
package com.brianway.learning.java8.effective.async;
2+
3+
import java.util.Arrays;
4+
import java.util.List;
5+
import java.util.concurrent.CompletableFuture;
6+
import java.util.concurrent.Executor;
7+
import java.util.concurrent.Executors;
8+
import java.util.concurrent.ThreadFactory;
9+
import java.util.stream.Collectors;
10+
import java.util.stream.Stream;
11+
12+
/**
13+
* 添加折扣后的版本
14+
* 构造同步和异步操作
15+
*/
16+
public class BestPriceFinder {
17+
18+
private final List<Shop> shops = Arrays.asList(new Shop("BestPrice"),
19+
new Shop("LetsSaveBig"),
20+
new Shop("MyFavoriteShop"),
21+
new Shop("BuyItAll"),
22+
new Shop("ShopEasy"));
23+
24+
private final Executor executor = Executors.newFixedThreadPool(shops.size(), new ThreadFactory() {
25+
@Override
26+
public Thread newThread(Runnable r) {
27+
Thread t = new Thread(r);
28+
t.setDaemon(true);
29+
return t;
30+
}
31+
});
32+
33+
public List<String> findPricesSequential(String product) {
34+
return shops.stream()
35+
.map(shop -> shop.getPrice(product))
36+
.map(Quote::parse)
37+
.map(Discount::applyDiscount)
38+
.collect(Collectors.toList());
39+
}
40+
41+
public List<String> findPricesParallel(String product) {
42+
return shops.parallelStream()
43+
.map(shop -> shop.getPrice(product))
44+
.map(Quote::parse)
45+
.map(Discount::applyDiscount)
46+
.collect(Collectors.toList());
47+
}
48+
49+
public List<String> findPricesFuture(String product) {
50+
List<CompletableFuture<String>> priceFutures = findPricesStream(product)
51+
.collect(Collectors.toList());
52+
53+
return priceFutures.stream()
54+
.map(CompletableFuture::join)
55+
.collect(Collectors.toList());
56+
}
57+
58+
/**
59+
* 分了三步:
60+
* 1. 获取价格
61+
* 2. 解析报价
62+
* 3. 为计算折扣价格构造 Future
63+
*/
64+
public Stream<CompletableFuture<String>> findPricesStream(String product) {
65+
return shops.stream()
66+
.map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor))
67+
.map(future -> future.thenApply(Quote::parse))
68+
.map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(
69+
() -> Discount.applyDiscount(quote), executor)));
70+
}
71+
72+
/**
73+
* 响应 CompletableFuture 的 completion 事件
74+
* 把 Util 类中的 delay 调一下效果更明显
75+
*/
76+
public void printPricesStream(String product) {
77+
long start = System.nanoTime();
78+
CompletableFuture[] futures = findPricesStream(product)
79+
.map(f -> f.thenAccept(s -> System.out.println(s + " (done in " + ((System.nanoTime() - start) / 1_000_000) + " msecs)")))
80+
.toArray(CompletableFuture[]::new);
81+
CompletableFuture.allOf(futures).join();
82+
System.out.println("All shops have now responded in " + ((System.nanoTime() - start) / 1_000_000) + " msecs");
83+
}
84+
85+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.brianway.learning.java8.effective.async;
2+
3+
import java.util.List;
4+
import java.util.function.Supplier;
5+
6+
/**
7+
* 测试每种实现方案的执行时间
8+
*/
9+
public class BestPriceFinderMain {
10+
11+
private static BestPriceFinder bestPriceFinder = new BestPriceFinder();
12+
13+
public static void main(String[] args) {
14+
execute("sequential", () -> bestPriceFinder.findPricesSequential("myPhone27S"));
15+
execute("parallel", () -> bestPriceFinder.findPricesParallel("myPhone27S"));
16+
execute("composed CompletableFuture", () -> bestPriceFinder.findPricesFuture("myPhone27S"));
17+
bestPriceFinder.printPricesStream("myPhone27S");
18+
}
19+
20+
private static void execute(String msg, Supplier<List<String>> s) {
21+
long start = System.nanoTime();
22+
System.out.println(s.get());
23+
long duration = (System.nanoTime() - start) / 1_000_000;
24+
System.out.println(msg + " done in " + duration + " msecs");
25+
}
26+
27+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.brianway.learning.java8.effective.async;
2+
3+
import static com.brianway.learning.java8.effective.async.Util.delay;
4+
import static com.brianway.learning.java8.effective.async.Util.format;
5+
6+
/**
7+
* 折扣类
8+
*/
9+
public class Discount {
10+
11+
public enum Code {
12+
NONE(0),
13+
SILVER(5),
14+
GOLD(10),
15+
PLATINUM(15),
16+
DIAMOND(20);
17+
18+
private final int percentage;
19+
20+
Code(int percentage) {
21+
this.percentage = percentage;
22+
}
23+
}
24+
25+
public static String applyDiscount(Quote quote) {
26+
return quote.getShopName() + " price is " + Discount.apply(quote.getPrice(), quote.getDiscountCode());
27+
}
28+
29+
private static double apply(double price, Code code) {
30+
delay();
31+
return format(price * (100 - code.percentage) / 100);
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.brianway.learning.java8.effective.async;
2+
3+
import static com.brianway.learning.java8.effective.async.Util.delay;
4+
5+
/**
6+
* 货币兑换类
7+
*/
8+
public class ExchangeService {
9+
10+
public enum Money {
11+
USD(1.0),
12+
EUR(1.35387),
13+
GBP(1.69715),
14+
CAD(.92106),
15+
MXN(.07683);
16+
17+
private final double rate;
18+
19+
Money(double rate) {
20+
this.rate = rate;
21+
}
22+
}
23+
24+
public static double getRate(Money source, Money destination) {
25+
return getRateWithDelay(source, destination);
26+
}
27+
28+
private static double getRateWithDelay(Money source, Money destination) {
29+
delay();
30+
return destination.rate / source.rate;
31+
}
32+
33+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package com.brianway.learning.java8.effective.async;
2+
3+
/**
4+
* 解析商店返回的字符串
5+
*/
6+
public class Quote {
7+
8+
private final String shopName;
9+
private final double price;
10+
private final Discount.Code discountCode;
11+
12+
public Quote(String shopName, double price, Discount.Code discountCode) {
13+
this.shopName = shopName;
14+
this.price = price;
15+
this.discountCode = discountCode;
16+
}
17+
18+
public static Quote parse(String s) {
19+
String[] split = s.split(":");
20+
String shopName = split[0];
21+
double price = Double.parseDouble(split[1]);
22+
Discount.Code discountCode = Discount.Code.valueOf(split[2]);
23+
return new Quote(shopName, price, discountCode);
24+
}
25+
26+
public String getShopName() {
27+
return shopName;
28+
}
29+
30+
public double getPrice() {
31+
return price;
32+
}
33+
34+
public Discount.Code getDiscountCode() {
35+
return discountCode;
36+
}
37+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package com.brianway.learning.java8.effective.async;
2+
3+
import static com.brianway.learning.java8.effective.async.Util.delay;
4+
import static com.brianway.learning.java8.effective.async.Util.format;
5+
6+
import java.util.Random;
7+
8+
/**
9+
* 商店类
10+
* 报价按照 `name:price:code` 的格式
11+
*/
12+
public class Shop {
13+
14+
private final String name;
15+
private final Random random;
16+
17+
public Shop(String name) {
18+
this.name = name;
19+
random = new Random(name.charAt(0) * name.charAt(1) * name.charAt(2));
20+
}
21+
22+
public String getPrice(String product) {
23+
double price = calculatePrice(product);
24+
Discount.Code code = Discount.Code.values()[random.nextInt(Discount.Code.values().length)];
25+
return name + ":" + price + ":" + code;
26+
}
27+
28+
public double calculatePrice(String product) {
29+
delay();
30+
return format(random.nextDouble() * product.charAt(0) + product.charAt(1));
31+
}
32+
33+
public String getName() {
34+
return name;
35+
}
36+
}

0 commit comments

Comments
 (0)