guava异步增强-ListenableFuture

jdk原生的future已经提供了异步操作,但是不能直接回调。guava对future进行了增强,核心接口就是ListenableFuture。如果已经开始使用了jdk8,可以直接学习使用原生的CompletableFuture,这是jdk从guava中吸收了精华新增的类。

guava 对 jdk 的异步增强可以通过看 MoreExecutorsFutures 两个类的源码入手

Guava 异步回调简单应用

	private final Executor executor = Executors.newFixedThreadPool(20);

    @GetMapping(value = "/doJob")
    public Response doJob() {
        // ListenableFutureTask 通过静态create方法返回实例, 还有一个重载方法, 不太常用
        ListenableFutureTask<String> task = ListenableFutureTask.create(() -> {
            log.info("模拟异步耗时任务, 休眠10秒");
            Thread.sleep(10000);
            return "Jaemon";
        });

        executor.execute(task);

        // 增加回调方法, MoreExecutors.directExecutor()返回guava默认的Executor, 执行回调方法不会新开线程, 所有回调方法都在当前线程做(可能是主线程或者执行ListenableFutureTask的线程, 具体可以看最后面的代码)
        // guava异步模块中参数有Executor的方法, 一般还会有一个没有Executor参数的重载方法, 使用的就是MoreExecutors.directExecutor()
        task.addListener(() -> {
            try {
                String result = task.get();
                log.info("获取异步结果, result=[{}]", result);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }, MoreExecutors.directExecutor());

        return Response.success();
    }

DirectExecutor 源码

public final class MoreExecutors {
	// ...
	
	public static Executor directExecutor() {
    	return DirectExecutor.INSTANCE;
	}

	// ...
}

enum DirectExecutor implements Executor {
  INSTANCE;

  @Override
  public void execute(Runnable command) {
    command.run();
  }

  @Override
  public String toString() {
    return "MoreExecutors.directExecutor()";
  }
}

一般使用异步模式的时候,都会用一个线程池来提交任务,不会像上面那样简单的开一个线程去做,那样效率太低下了,所以需要说说guava对jdk原生线程池的封装。guava对原生线程池的增强都在MoreExecutor类中,guava对ExecutorService和ScheduledExecutorService的增强类似,这里只介绍ExecutorService的增强。

Guava的异步回调

	private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            10, 20, 30, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new CustomizableThreadFactory("asyncThread-"),
            new ThreadPoolExecutor.DiscardPolicy());

    @GetMapping(value = "/doJob")
    public Response doJob() throws Exception {
        final int count = 10;
        List<String> seqNos = new ArrayList<>();
        for (int i = 0; i < count; i++) {
            seqNos.add(String.valueOf(i));
        }
        ListenableFuture<String> listenableFuture;
        // guava的接口ListeningExecutorService继承了jdk原生ExecutorService接口,重写了submit方法,修改返回值类型为ListenableFuture
        ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(threadPoolExecutor);

		// 获得一个随着jvm关闭而关闭的线程池,通过Runtime.getRuntime().addShutdownHook(hook)实现
        // 修改ThreadFactory为创建守护线程,默认jvm关闭时最多等待120秒关闭线程池,重载方法可以设置时间
//        ExecutorService newPoolExecutor = MoreExecutors.getExitingExecutorService(threadPoolExecutor);

        // 只增加关闭线程池的钩子,不改变ThreadFactory
//        MoreExecutors.addDelayedShutdownHook(threadPoolExecutor, 120, TimeUnit.SECONDS);

        final CountDownLatch countDownLatch = new CountDownLatch(count);
        long startTime = System.currentTimeMillis();

        for (String seqNo : seqNos) {
            listenableFuture = listeningExecutorService.submit(() -> {
                log.info("seqNo=[{}]", seqNo);
                if ("5".equals(seqNo)) {
                    Thread.sleep(10000);
                    throw new IllegalArgumentException("无效的参数异常");
                }
                return String.format("result seqNo=%s", seqNo);
            });


            /**
             * 注册任务回调函数
             * 底层其实就是对于 listenableFuture.addListener() 方法的封装
             * 对于可以在任何线程中安全执行的快速、轻量级侦听器(不是很耗时和占用资源), 可以考虑使用 MoreExecutors.directExecutor(), 否则请避免使用
             */
            Futures.addCallback(listenableFuture, new FutureCallback<String>() {
                @Override
                public void onSuccess(String result) {
                    log.info("获取的返回结果为=[{}]", result);
                    countDownLatch.countDown();
                }
                @Override
                public void onFailure(Throwable throwable) {
                    log.error("抛异常啦=[{}]", throwable.getMessage());
                    countDownLatch.countDown();
                }
            }, MoreExecutors.directExecutor());
        }

        log.info("等待所有任务执行完毕");
        countDownLatch.await(3, TimeUnit.SECONDS);
        log.info("[{}]个异步任务共计耗时=[{}]ms", count, (System.currentTimeMillis() - startTime));
        return Response.success();
    }

Guava中Futures对于Future扩展

  • transform: 对于ListenableFuture的返回值进行转换
  • allAsList: 对多个ListenableFuture的合并,返回一个当所有Future成功时返回多个Future返回值组成的List对象。注:当其中一个Future失败或者取消的时候,将会进入失败或者取消。
  • successfulAsList: 和allAsList相似,唯一差别是对于失败或取消的Future返回值用null代替。不会进入失败或者取消流程。
  • immediateFuture/immediateCancelledFuture: 立即返回一个待返回值的ListenableFuture。
  • makeChecked: 将ListenableFuture 转换成CheckedFuture。CheckedFuture 是一个ListenableFuture ,其中包含了多个版本的get 方法,方法声明抛出检查异常.这样使得创建一个在执行逻辑中可以抛出异常的Future更加容易
  • JdkFutureAdapters.listenInPoolThread(future): guava同时提供了将JDK Future转换为ListenableFuture的接口函数。

Guava 异步链式执行

	private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            10, 20, 30, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new CustomizableThreadFactory("asyncThread-"),
            new ThreadPoolExecutor.DiscardPolicy());


    @GetMapping(value = "/doJob")
    public Response doJob() throws Exception {
        ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(threadPoolExecutor);

        ListenableFutureTask<String> task1 = ListenableFutureTask.create(() -> {
            TimeUnit.SECONDS.sleep(5);
            log.info("任务1执行中...");
            return "1";
        });

        listeningExecutorService.execute(task1);

        // 当task1执行完毕会回调执行Function的apply方法, 如果有task1有异常抛出, 则task2也抛出相同异常, 不执行apply
        ListenableFuture<String> task2 = Futures.transform(task1, input -> {
            log.info("任务2执行中, input=[{}]", input);
            return input + "-Answer";
        }, threadPoolExecutor);
        
        ListenableFuture<String> task3 = Futures.transform(task2, input -> {
            log.info("任务3执行中, input=[{}]", input);
            return input + "-Jaemon";
        }, threadPoolExecutor);

        // 处理最终的异步任务
        Futures.addCallback(task3, new FutureCallback<String>() {
            @Override
            public void onSuccess(String result) {
                log.info("成功啦, result=[{}]", result);
            }

            @Override
            public void onFailure(Throwable t) {
                log.error("失败啦。", t);
            }
        }, threadPoolExecutor);


        return Response.success();
    }

运行结果

2019-12-09 19:56:19.735 [Answer-AI-L] [jaemon-service]  INFO 9388 -- [  asyncThread-1] c.j.controller.MyController  :[93] 任务1执行中...
2019-12-09 19:56:19.740 [Answer-AI-L] [jaemon-service]  INFO 9388 -- [  asyncThread-2] c.j.controller.MyController  :[101] 任务2执行中, input=[1]
2019-12-09 19:56:19.742 [Answer-AI-L] [jaemon-service]  INFO 9388 -- [  asyncThread-3] c.j.controller.MyController  :[106] 任务3执行中, input=[1-Answer]
2019-12-09 19:56:19.744 [Answer-AI-L] [jaemon-service]  INFO 9388 -- [  asyncThread-4] c.j.controller.MyController  :[114] 成功啦, result=[1-Answer-Jaemon]

并行编程 Futures

	private final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            10, 20, 30, TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(100),
            new CustomizableThreadFactory("asyncThread-"),
            new ThreadPoolExecutor.DiscardPolicy());


    @GetMapping(value = "/doJob")
    public Response doJob() throws Exception {
        ListeningExecutorService listeningExecutorService = MoreExecutors.listeningDecorator(threadPoolExecutor);

        ListenableFuture<Integer> future1 = listeningExecutorService.submit(() -> {
            log.info("任务1执行中...");
            Thread.sleep(2000);
            return 1;
        });

        ListenableFuture<Integer> future2 = listeningExecutorService.submit(() -> {
            log.info("任务2执行中...");
            Thread.sleep(3000);
            // 如果此处抛出异常?
//            if (true)
//                throw new RuntimeException("dddddddddd");
            return 2;
        });

        // 对多个 ListenableFuture 的合并, 如果某一个 ListenableFuture 执行抛出异常, 则失败
        final ListenableFuture<List<Integer>> allFutures = Futures.allAsList(future1, future2);
        // 和 allAsList 相似, 但某一个 ListenableFuture 执行抛出异常, 则返回null, 不影响程序继续执行
//        final ListenableFuture<List<Integer>> allFutures = Futures.successfulAsList(future1, future2);

        // 对于ListenableFuture的返回值进行转换
        final ListenableFuture<String> transform = Futures.transform(allFutures, results -> {
            log.info("results=[{}]", results);
            return JSON.toJSONString(results);
        }, threadPoolExecutor);
//		final ListenableFuture<String> transform = Futures.transformAsync(allFutures, results -> Futures.immediateFuture(JSON.toJSONString(results)), threadPoolExecutor);

        // 处理最终的异步任务
        Futures.addCallback(transform, new FutureCallback<String>() {
            @Override
            public void onSuccess(String result) {
                log.info("成功啦, result=[{}]",result);
            }

            @Override
            public void onFailure(Throwable thrown) {
                log.error("失败啦.", thrown);
            }
        }, threadPoolExecutor);

        log.info("transform result=[{}]", transform.get());

        return Response.success();
    }

运行结果

2019-12-10 11:27:23.479 [Answer-AI-L] [jaemon-service]  INFO 7104 -- [  asyncThread-1] c.j.controller.MyController  :[90] 任务1执行中...
2019-12-10 11:27:23.479 [Answer-AI-L] [jaemon-service]  INFO 7104 -- [  asyncThread-2] c.j.controller.MyController  :[96] 任务2执行中...
2019-12-10 11:27:26.480 [Answer-AI-L] [jaemon-service]  INFO 7104 -- [  asyncThread-3] c.j.controller.MyController  :[111] results=[[1, 2]]
2019-12-10 11:27:26.527 [Answer-AI-L] [jaemon-service]  INFO 7104 -- [nio-8888-exec-1] c.j.controller.MyController  :[128] transform result=[[1,2]]
2019-12-10 11:27:26.527 [Answer-AI-L] [jaemon-service]  INFO 7104 -- [  asyncThread-4] c.j.controller.MyController  :[119] 成功啦, result=[[1,2]]
  • Futures.transform() 和 Futures.addCallback() 都是对 addListener 做了封装,进行回调的设置,但是 transform更适合用在链式处理的中间过程,addCallback更适合用在处理最终的结果上。
  • Futures.transform()和Futures.transformAsync()的区别在于一个参数为Function,一个是AsyncFuntion,AsyncFuntion的apply方法返回值类型也是ListenableFuture,也就是回调方法也是异步的。

参考网址

发布了152 篇原创文章 · 获赞 27 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/u010979642/article/details/103421275