JDK8新特性
CompletableFutrue
实现线程的三种方法:
- 继承Thread类;
- 实现Runnable接口;
- 实现Callable<返回类型>接口。
实现Runnable接口没有返回值;Callable接口可以通过Future<T,>获取返回值。
使用Future
/*
* Callable接口是一个泛型接口,可以返回指定类型的结果。
*/
class Task implements Callable<String> {
public String call() throws Exception {
return longTimeCalculation();
}
}
ExecutorService executor = Executors.newFixedThreadPool(4);
// 定义任务:
Callable<String> task = new Task();
// 提交任务并获得Future:
Future<String> future = executor.submit(task);
// 从Future获取异步执行返回的结果:
String result = future.get(); // 可能阻塞
System.out.println(result);
当我们提交一个Callable任务后,我们会同时获得一个Future对象,然后,我们在主线程某个时刻调用Future对象的get()方法,就可以获得异步执行的结果。在调用get()时,如果异步任务已经完成,我们就直接获得结果。如果异步任务还没有完成,那么get()会阻塞,直到任务完成后才返回结果。
使用CompletableFuture
从Java 8开始引入了CompletableFuture,它针对Future做了改进,可以传入回调对象,当异步任务完成或者发生异常时,自动调用回调对象的回调方法。
核心API:
1. completableFuture()/[supply|run](Async)/
2. Then[apply|accept|run](Both|Euther)(Async)
3. get() vs join()
4. allOf/anyOf
public class Main {
public static void main(String[] args) throws Exception {
// 创建异步执行任务:
CompletableFuture<Double> cf = CompletableFuture.supplyAsync(Main::fetchPrice);
// 如果执行成功:
cf.thenAccept((result) -> {System.out.println("price: " + result);});
// 如果执行异常:
cf.exceptionally((e) -> {e.printStackTrace(); return null;});
// 主线程不要立刻结束,否则CompletableFuture默认使用的线程池会立刻关闭:
Thread.sleep(200);
}
static Double fetchPrice() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
}
if (Math.random() < 0.3) {
throw new RuntimeException("fetch price failed!");
}
return 5 + Math.random() * 20;
}
}
- CompletableFuture.supplyAsync()实现的,它需要一个实现了Supplier接口的对象;
- 完成时,thenAccept()会调用Consumer对象;
- 异常时,exceptionally()会调用Function对象。
可见CompletableFuture的优点是:
- 异步任务结束时,会自动回调某个对象的方法;
- 异步任务出错时,会自动回调某个对象的方法;
- 主线程设置好回调后,不再关心异步任务的执行。
串行执行
public class Main {
public static void main(String[] args) throws Exception {
// 第一个任务:
CompletableFuture<String> cfQuery = CompletableFuture.supplyAsync(() -> {
return queryCode("中国石油");
})
// cfQuery成功后继续执行下一个任务:
.thenApplyAsync((code) -> {
return fetchPrice(code);
});
// 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) {
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
}
return "601857";
}
static Double fetchPrice(String code, String url) {
try {
Thread.sleep((long) (Math.random() * 100));
} catch (InterruptedException e) {
}
return 5 + Math.random() * 20;
}
}
总结:
ompletableFuture可以指定异步处理流程:
- thenAccept()处理正常结果;
- exceptional()处理异常结果;
- thenApplyAsync()用于串行化另一个CompletableFuture;
- anyOf()和allOf()用于并行化多个CompletableFuture。
Optional
Optional 类主要解决的问题是臭名昭著的空指针异常(NullPointerException)。
如:
// 有可能报空指针异常
String isocode = user.getAddress().getCountry().getIsocode().toUpperCase();
// 安全的,没有异常,但是代码冗余,不易维护
if (user != null) {
Address address = user.getAddress();
if (address != null) {
Country country = address.getCountry();
if (country != null) {
String isocode = country.getIsocode();
if (isocode != null) {
isocode = isocode.toUpperCase();
}
}
}
}
// 使用Optional后
String result = Optional.ofNullable(user)
.flatMap(u -> u.getAddress())
.flatMap(a -> a.getCountry())
.map(c -> c.getIsocode())
.orElse("default");
创建 Optional 实例
Optional<Object> empty = Optional.empty();
//empty.get(); // 报NoSuchElementException异常
Optional<Object> o = Optional.of(new Object());
Optional<Object> o1 = Optional.of(null);
o1.get(); // 报NullPointerException异常
Optional<Object> o2 = Optional.ofNullable(null);
orElse 和orElseGet
Optional 类提供了 API 用以返回对象值,或者在对象为空的时候返回默认值。
private User createNewUser() {
logger.debug("Creating New User");
return new User("extra@gmail.com", "1234");
}
- 当user为空的时候,两种方法都调用了 createNewUser() 方法。
User user = null;
logger.debug("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
logger.debug("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
- 当user不为空的时候,orElseGet() 方法不调用 createNewUser()方法。
User user = new User("john@gmail.com", "1234");
logger.info("Using orElse");
User result = Optional.ofNullable(user).orElse(createNewUser());
logger.info("Using orElseGet");
User result2 = Optional.ofNullable(user).orElseGet(() -> createNewUser());
map和flatMap
区别:map的返回值Optional对象是在map方法里面自动封装好了。而flatMap方法是自己的实现的逻辑中自己封装。
看源码:
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
// 这是返回的时候自动把返回值包装好了
return Optional.ofNullable(mapper.apply(value));
}
}
public<U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper) {
Objects.requireNonNull(mapper);
if (!isPresent())
return empty();
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
- map() 对值调用作为参数的函数,然后将返回的值包装在 Optional 中。
User user = new User("anna@gmail.com", "1234");
String email = Optional.ofNullable(user)
.map(u -> u.getEmail()).orElse("default@gmail.com");
assertEquals(email, user.getEmail());
- flatMap() 也需要函数作为参数,并对值调用这个函数,然后直接返回结果。
public class User {
private String position;
public Optional<String> getPosition() {
return Optional.ofNullable(position);
}
}
User user = new User("anna@gmail.com", "1234");
user.setPosition("Developer");
String position = Optional.ofNullable(user)
.flatMap(u -> u.getPosition()).orElse("default");
assertEquals(position, user.getPosition().get());
函数组合compose和andthen