Future 执行异步任务

public class AnswerApp {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newCachedThreadPool();

Future<Integer> future = executorService.submit(() -> {
logger("模拟异步耗时任务开始...");
try {
// 模拟耗时任务
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger("模拟异步耗时任务结束!!!");
return 10;
});

//当前线程继续做其他事情
logger("处理其他事情...\n");

// 当前主线程阻塞, 直到获取异步操作的结果
Integer result = future.get();

logger("result: " + result);
}
}

​程序运行结果​

2019-06-28 18:20:22:618 模拟异步耗时任务开始...
2019-06-28 18:20:22:618 处理其他事情...

2019-06-28 18:20:27:622 模拟异步耗时任务结束!!!
2019-06-28 18:20:27:622 result: 10

 

FutureTask 执行异步任务

public class AnswerApp {
public static void main(String[] args) throws Exception {
FutureTask<Integer> futureTask = new FutureTask<>(() -> {
logger("异步耗时任务开始...");
// 模拟耗时任务
Thread.sleep(5000);
return 100;
});

ExecutorService executorService = Executors.newCachedThreadPool();
executorService.execute(futureTask);

// 阻塞, 直到获取异步任务结果
logger("result: " + futureTask.get());
executorService.shutdown();
}
}

​程序运行结果​

2019-06-28 18:14:46:272 异步耗时任务开始...
2019-06-28 18:14:51:274 result: 100

Future 接口可以执行异步任务,但却有其局限性。 它很难直接表述多个 Future 结果之间的依赖性。

  • 将多个异步计算的结果合并成一个
  • 等待 Future 集合中的所有任务都完成
  • Future 任务完成以后触发执行动作

 

CompletableFuture 执行异步任务

public class AnswerApp {
public static void main(String[] args) throws Exception {
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
logger("获取数据(耗时任务)开始...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger("已经拿到数据啦~~~");
return 10;
}).thenApply((data) -> {
logger(MessageFormat.format("对拿到的·数据{0}·进行加工处理...", data));
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return data * data;
}).whenCompleteAsync((r, e) -> logger("最终数据: " + r));

logger("主线程正在执行耗时任务\n");
Thread.sleep(3000);
logger("主线程耗时任务执行完成\n");

completableFuture.get();
}
}

​程序运行结果​

2019-06-28 19:18:40:947 获取数据(耗时任务)开始...
2019-06-28 19:18:40:947 主线程正在执行耗时任务

2019-06-28 19:18:43:952 主线程耗时任务执行完成

2019-06-28 19:18:45:951 已经拿到数据啦~~~
2019-06-28 19:18:45:971 对拿到的·数据10·进行加工处理...
2019-06-28 19:18:47:972 最终数据: 100

 

结合两个 CompletableFuture

public class AnswerApp {
public static void main(String[] args) throws Exception {
CompletableFuture completableFuture = CompletableFuture.supplyAsync(() -> {
logger("数据提取数据(耗时任务)...");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger("数据已经拿到啦~~~");
return 10;
}).thenCompose(preRlt -> CompletableFuture.supplyAsync(() -> {
logger("我拿到了上一步任务的数据啦~ preRlt: " + preRlt);
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return 10 + preRlt;
}));

logger("最终结果数据: " + completableFuture.get());
}
}

​程序运行结果​

2019-06-28 18:31:44:491 数据提取数据(耗时任务)...
2019-06-28 18:31:49:496 数据已经拿到啦~~~
2019-06-28 18:31:49:499 我拿到了上一步任务的数据啦~ preRlt: 10
2019-06-28 18:31:54:501 最终结果数据: 20

 

并行执行多个异步任务

public class AnswerApp {
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();

CompletableFuture<Integer> completableFuture1 = CompletableFuture.supplyAsync(() -> {
logger("耗时·任务1·准备开始...");
try {
// 模拟耗时任务1
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger("我已经拿到耗时·任务1·的数据啦~~~");
return 10;
});

CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(() -> {
logger("耗时·任务2·准备开始...");
try {
// 模拟耗时任务2
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
logger("我已经拿到耗时·任务2·的数据啦~~~");
return 20;
});

CompletableFuture<Void> combinedFuture = CompletableFuture.allOf(completableFuture1, completableFuture2);

// 这个方法不会合并结果,可以看到他的返回值是 Void 类型
combinedFuture.get();

Optional<Integer> sum = Stream.of(completableFuture1, completableFuture2).map(CompletableFuture::join).reduce((x, y) -> x + y);
logger("两个任务的结果和: " + String.valueOf(sum.orElse(0)));

logger("程序总共耗时 " + (System.currentTimeMillis() - start) / 1000 + " s");
}

​程序运行结果​

2019-06-28 19:04:20:936 耗时·任务2·准备开始...
2019-06-28 19:04:20:936 耗时·任务1·准备开始...
2019-06-28 19:04:23:939 我已经拿到耗时·任务2·的数据啦~~~
2019-06-28 19:04:25:939 我已经拿到耗时·任务1·的数据啦~~~
2019-06-28 19:04:25:943 两个任务的结果和: 30
2019-06-28 19:04:25:943 程序总共耗时 5 s

 

异常处理

public class AnswerApp {
public static void main(String[] args) throws Exception {
String name = null;

// 第一个参数为 CompletableFuture 返回的结果, 第二个参数为抛出的异常
// s: CompletableFuture 返回的结果, t: 抛出的异常
CompletableFuture completableFuture1 = CompletableFuture.supplyAsync(() -> {
Assert.notNull(name, "name is null");
return MessageFormat.format("hello {0}", name);
}).handle((s, t) -> s != null ? s : t.getMessage());

logger("最终拿到的结果1: " + completableFuture1.get());

System.out.println();

String userName = "answer";
CompletableFuture completableFuture2 = CompletableFuture.supplyAsync(() -> {
Assert.notNull(userName, "userName is null");
return MessageFormat.format("hello {0}", userName);
}).handle((s, t) -> s != null ? s : t.getMessage());

logger("最终拿到的结果2: " + completableFuture2.get());
}
}

​程序运行结果​

2019-06-28 19:07:38:136 最终拿到的结果1: java.lang.IllegalArgumentException: name is null

2019-06-28 19:07:38:139 最终拿到的结果2: hello answer

 

日志打印方法

private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS");

private static void logger(String log) {
System.out.println(MessageFormat.format("{0} {1}", LocalDateTime.now().format(FORMATTER), log));
}

 

Reference