异步编程的概念
由于每个线程同时只能处理一个请求,所以为了提高系统系统性能,我们就需要引入更多的线程来实现并行化处理。但是多线程下对共享资源进行访问是,不可避免会引入资源争用和并发问题;另外,每个进程可使用的线程个数是有限的,不可能通过无限增加线程数来提高系统性能;而且,使用同步阻塞编程的方式还会浪费资源,比如发起网络IO请求是,调用线程就会阻塞,等待响应结果,但是,这时候调用线程明明可以去做其他事情,等网络IO响应结果返回在对结果进行处理。
异步编程的场景
在日常开发中我们经常需要异步的处理一下事情,而不需要知道异步任务的结果。比如在调用线程里面异步打印日志,为了不让日志打印阻塞调用线程,会把日志设置为异步方式。
有时候我们还需要在主线程等待异步任务的执行结果,这时候Future开始登场。比如调用线程要等待任务A执行完毕后顺序执行任务B,并且把两者的执行结果拼接起来给前端使用,如果调用线程是同步调用,则整个过程耗时等于任务A+任务B的耗时;如果使用异步编程Future的方式,则整个过程的耗时等于任务A和任务B的最大耗时;这样响应时间大大缩短。
使用Future确实大大缩短了异步任务的执行结果,但是获取结果还是会阻塞调用线程,并没有完全实现异步处理,所以在JDK8中引入了CompletableFuture来实现完全异步化。CompletableFuture类允许以非阻塞方式和基于通知的方式处理结果,其通过设置回调函数的方式,让主线程彻底解放出来,实现真正意义上的异步处理。
使用CompletableFuture时,当异步单元返回futureB后,调用线程可以在其上调用whenComplete方法设置回调函数action,然后调用线程就会马上返回,等异步任务执行完成后会使用异步线程来执行回调函数action,而不需要调用线程干预等待。
JDK8还引入了Stream,旨在有效的处理数据流,这样结合CompletableFuture可以完美的实现异步编程。但是流只能使用一次,并且缺少与时间相关的操作,虽然可以执行并行计算,但无法指定使用的线层池。同时,也没有设计用于处理延迟的操作。
上面我们说的都是单JVM内的异步编程,下面我们介绍一下跨网络的异步编程。NIO的出现让实现异步的功能变的简单,而高性能异步、基于事件驱动的网络编程框架Netty的出现让我们从繁杂的NIO中解放出来。而Netty的异步非阻塞能力与CompletableFuture结合则可以轻松的实现网络请求的异步调用。
虽然Web Servlet具有异步处理能力,但是其异步是不彻底的,因为受制于Servlet规范本身,比如其规范是同步(Filter、Servlet)或者阻塞的(getParameter、getPart)。所以WebFlux就应运而生。
为了更好的处理异步编程,降低异步编程的成本,一些框架也应运而生,如Disruptor、Akka、rmq等
总结
我们简单介绍了一下什么是异步编程,让大家对异步编程有了一个简单理解。接下来我们会一步一步介绍异步编程。
用技术改变世界,大家一起加油🆙