首页 > 编程知识 正文

异步编程实战,java异步编程等待结果

时间:2023-05-04 17:40:20 阅读:155352 作者:3212

本文来自网易云社区。

Java异步编程引言

Java异步编程实际上是充分利用了计算机的CPU资源,不希望被长时间运行的任务屏蔽主程序。 这些耗时的任务包括I/o操作、远程调用和高密度计算任务。 如果不使用多线程异步编程,耗时的子任务会阻止系统,完成主函数任务所需的时间会大大增加。 提供丰富的API以实现Java和多线程异步编程。 来自NIO、Future、CompletableFuture、Fork/Join和parrallelStream。 另外,谷歌的guava框架提供了ListenableFuture和Spring的@Async来简化异步编程。

项目实战

首先,让我们从自己创建的项目中实际具体说明多线程的使用。 要观察此系统中资源池的显示,需要展示四种节点类型的数据。 大致分为两种节点。 一个是计算节点,需要计算vcpu、内存、存储等,包括图的前三个节点。 另一个是网络资源、用户路由和占用带宽等计算,以及图中最后的节点。 展示图如下

核心任务分析

资源统计任务包括三种耗时的操作,在调用底层接口时需要多线程异步获取结果。 1、调用底层云网络接口

2 .调用底层云主机接口获取所有宿主机的后端存储

3、调用底层云主机接口获取所有可用域的下级主机列表

流程的实现

项目主要采用两个线程池实现异步化编程。 定制线程池,以指定要在Spring框架的@Async注释中使用的线程池以及要在CompletableFuture中使用的线程池。 初始化线程池时必须设置拒绝任务策略。 通常,选择CallerRunsPolicy可在线程池的任务缓存队列已满且线程池中的线程数达到最大线程池大小时将任务传递给调用线程。

@Component

publicclassoperationasyncexecutor

private int corePoolSize=3;

私有int maxpools ize=6;

私有队列容量=50;

publicstaticstringthreadnameprefix=' op-async-exec-';

publicstaticfinalstringoperation _ async _ executor=' operationsyncexecutor ';

@ bean (value=operation _ async _ executor ) )。

公共执行管理器opasyncexecutor (

threadpooltaskexecutorexecutor=newthreadpooltaskexecutor (;

executor.setcorepoolsize (corepoolsize;

executor.setmaxpoolsize (maxpools ize;

executor.setqueuecapacity;

executor.setthreadnameprefix (thread name prefix;

executor.setrejectedexecutionhandler (newthreadpoolexecutor.callerrunspolicy () );

executor.initialize (;

返回执行程序;

}

}

使用Spring框架提供的@Async注释实现异步处理以获取所有路由信息。

@ async (操作同步执行程序) )。

publicfuturegetallnetworkrouter (stringpartnertenantid ) (

List routers=Lists.newArrayList (;

listalltenantids=ncerequesthelper.get tenants (partnertenantid );

alltenantids.isempty () ) ) )。

返回wasyncresult (routers;

}

routers=middletierrequesthelper.get routers (alltenantids;

返回wasyncresult (routers;

}

指定要执行异步任务的线程池,并获取所有宿主机的存储。

completablefuturehoststoragefuture=completablefuture.supply async (

returnmiddletierrequesthelper.geth

ostStorage();

}, opSyncExecutor);

本统计任务最耗时的是获取可用域下的宿主机列表,由于底层接口存在优化空间,调用该接口需要2.5s左右。而每个节点下又有3个可用域,如果采用顺序执行,那么资源统计的接口将达到10s以上,这是不可以忍受的。所有这里我们采用Executors.newCachedThreadPool()来获取线程池。来充分利用多线程异步化,来提高接口性能。

List>>> futures = Lists.newArrayList();

getAzByService(ServiceName.COMMON).forEach(az -> {

futures.add(executorService.submit(new QueryHostTask(ServiceName.COMMON, az)));

});

getAzByService(ServiceName.RDS).forEach(az -> {

futures.add(executorService.submit(new QueryHostTask(ServiceName.RDS, az)));

});

getAzByService(ServiceName.MONGO).forEach(az -> {

futures.add(executorService.submit(new QueryHostTask(ServiceName.MONGO, az)));

});

项目实战结果整个调用过程如下

目前资源统计的接口是2.6s以内,耗时在还是在底层接口上,统计任务控制在50ms以内。 关于异步编程,个人体会有以下几点:一定将并行的任务粒度控制到最细。保证多线程里运行的子任务是最细的,不可以再拆分。

能够并行处理的,千万不要串行。

可以使用多个线程池,来区分不能类型的任务,充分利用线程池来提高性能。

Java异步编程拓展

关于异步编程,项目中在异步是都会调用future的get()方法,来阻塞主线程,获取最后的异步结果。在另外一种多线程编程下,可以采用监听模式,来实现在得到异步结果的处理。比如guava的ListenableFuture的callback机制以及JDK8中CompletableFuture的thenAccept、thenApply等方法都可以实现监听的效果。

parrallelStream和CompletableFuture的比较parrallelStream适合使用计算密集型并且没有I/O的任务,比较简单就能实现多线程处理,而且充分利用了计算机的CPU资源。因为parrallelStream底层使用的是默认的ForkJoinPool,该线程池的线程数和CPU的核心数目一致,如果线程中需要长时间的I/O,就使得其他需要使用并行流的任务阻塞。parrallelStream如果计算量比较大的话,可以采用自定义ForkJoinPool的方式,来增大线程池的线程数。

CompletableFuture适合在并行的工作单元涉及等待I/O的操作,如比较耗时的网络请求调用。而且CompletableFuture比较灵活,有多个静态方法来完成异步结果返回之后的操作。指的注意的是,CompletableFuture的join方法相当于Future的get方法,都会阻塞住调用线程。

关于两者的性能比较,大家可以参考

总结

在我们的实际项目中,需要多用Java异步编程,来提高性能。需要注意并行的工作单元的切分,以及注意有没有共享变量的使用。如果有不合理的地方,大家多指正。

本文来自网易实践者社区,经作者yxdxy授权发布。

了解网易云 :

更多网易研发、产品、运营经验分享请访问网易云社区。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。