Java 8 Future、FutureTask、CompletableFuture 异步任务

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);
    }
}

程序运行结果java

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();
    }
}

程序运行结果web

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();
    }
}

程序运行结果svg

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());
    }
}

程序运行结果spa

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