java异步编程

一.什么是异步?为什么要用它?

异步编程提供了一个非阻塞的,事件驱动的编程模型。 这种编程模型利用系统中多核执行任务来提供并行,因此提供了应用的吞吐率。此处吞吐率是

指在单位时间内所做任务的数量。 在这种编程方式下, 一个工作单元将独立于主应用线程而执行, 并且会将它的状态通知调用线程:成功,处理中或者失败。

我们需要异步来消除阻塞模型。其实异步编程模型可以使用同样的线程来处理多个请求, 这些请求不会阻塞这个线程。 想象一个应用正在使用的线程正在执行任务,
然后等待任务完成才进行下一步。 log框架就是一个很好的例子:典型地你想将异常和错误日志记录到一个目标中, 比如文件,数据库或者其它类似地方。
你不会让你的程序等待日志写完才执行,否则程序的响应就会受到影响。 相反,如果对log框架的调用是异步地,应用就可以并发执行其它任务而无需等待。这是一个非阻塞执行的例子。

二.异步编程

考虑有一个耗时的操作,操作完后会返回一个结果(不管是正常结果还是异常),程序如果想拥有比较好的性能不可能由线程去等待操作的完成,而是应该采用listener模式。jdk并发包里的Future代表了未来的某个结果,当我们向线程池中提交任务的时候会返回该对象。代码例子:

  1. /** 
  2.  * jdk1.8之前的Future 
  3.  *  
  4.  * @author Administrator 
  5.  * 
  6.  */  
  7. public class JavaFuture {  
  8.     public static void main(String[] args) throws Throwable, ExecutionException {  
  9.         ExecutorService executor = Executors.newFixedThreadPool(1);  
  10.         // Future代表了线程执行完以后的结果,可以通过future获得执行的结果  
  11.         // 但是jdk1.8之前的Future有点鸡肋,并不能实现真正的异步,需要阻塞的获取结果,或者不断的轮询  
  12.         // 通常我们希望当线程执行完一些耗时的任务后,能够自动的通知我们结果,很遗憾这在原生jdk1.8之前  
  13.         // 是不支持的,但是我们可以通过第三方的库实现真正的异步回调  
  14.         Future<String> f = executor.submit(new Callable<String>() {  
  15.   
  16.             @Override  
  17.             public String call() throws Exception {  
  18.                 System.out.println("task started!");  
  19.                 Thread.sleep(3000);  
  20.                 System.out.println("task finished!");  
  21.                 return "hello";  
  22.             }  
  23.         });  
  24.   
  25.         //此处阻塞main线程  
  26.         System.out.println(f.get());  
  27.         System.out.println("main thread is blocked");  
  28.     }  
  29. }  

如果想获得耗时操作的结果,可以通过get方法获取,但是该方法会阻塞当前线程,我们可以在做完剩下的某些工作的时候调用get方法试图去获取结果,也可以调用非阻塞的方法isDone来确定操作是否完成,这种方式对流程的控制很混乱,但是在jdk1.8之前只提供了这种笨拙的实现方式,以至于很多高性能的框架都实现了自己的一套异步框架,比如Netty和Guava,下面分别介绍下这三种异步的实现方式(包括jdk1.8)。
1.首先是Guava中的实现方式:
  1. /** 
  2.  * Guava中的Future 
  3.  *  
  4.  * @author Administrator 
  5.  * 
  6.  */  
  7. public class GuavaFuture {  
  8.     public static void main(String[] args) {  
  9.         ExecutorService executor = Executors.newFixedThreadPool(1);  
  10.   
  11.         // 使用guava提供的MoreExecutors工具类包装原始的线程池  
  12.         ListeningExecutorService listeningExecutor = MoreExecutors.listeningDecorator(executor);  
  13.         //向线程池中提交一个任务后,将会返回一个可监听的Future,该Future由Guava框架提供  
  14.         ListenableFuture<String> lf = listeningExecutor.submit(new Callable<String>() {  
  15.   
  16.             @Override  
  17.             public String call() throws Exception {  
  18.                 System.out.println("task started!");  
  19.                 //模拟耗时操作  
  20.                 Thread.sleep(3000);  
  21.                 System.out.println("task finished!");  
  22.                 return "hello";  
  23.             }  
  24.         });  
  25.         //添加回调,回调由executor中的线程触发,但也可以指定一个新的线程  
  26.         Futures.addCallback(lf, new FutureCallback<String>() {  
  27.   
  28.             //耗时任务执行失败后回调该方法  
  29.             @Override  
  30.             public void onFailure(Throwable t) {  
  31.                 System.out.println("failure");  
  32.             }  
  33.               
  34.             //耗时任务执行成功后回调该方法  
  35.             @Override  
  36.             public void onSuccess(String s) {  
  37.                 System.out.println("success " + s);  
  38.             }  
  39.         });  
  40.           
  41.         //主线程可以继续做其他的工作  
  42.         System.out.println("main thread is running");  
  43.     }  
  44. }  

Guava提供了一套完整的异步框架,核心是可监听的Future,通过注册监听器或者回调方法实现及时获取操作结果的能力。需要提一点的是,假设添加监听的时候耗时操作已经执行完了,此时回调方法会被立即执行并不会丢失。想探究其实现方式的话可以跟一下源码,底层的原理并不难。

注:Guava 是一个 Google 的基于java1.6的类库集合的扩展项目,包括 collections, caching, primitives support, concurrency libraries, common annotations, string processing, I/O, 等等. 这些高质量的 API 可以使你的JAVa代码更加优雅,更加简洁,让你工作更加轻松愉悦。

2.谈到异步编程就不得不提一下Promise,很多函数式语言比如js原生支持Promise,但是在java界也有一些promise框架,其中就有大名鼎鼎的Netty。从Future、Callback到Promise甚至线程池,Netty实现了一套完整的异步框架,并且netty代码中也大量使用了Promise,下面是Netty中的例子:

  1. /** 
  2.  * netty中的promise 
  3.  *  
  4.  * @author Administrator 
  5.  * 
  6.  */  
  7. public class PromiseTest {  
  8.     @SuppressWarnings({ "unchecked""rawtypes" })  
  9.     public static void main(String[] args) throws Throwable {  
  10.         //线程池  
  11.         EventExecutorGroup group = new DefaultEventExecutorGroup(1);  
  12.         //向线程池中提交任务,并返回Future,该Future是netty自己实现的future  
  13.         //位于io.netty.util.concurrent包下,此处运行时的类型为PromiseTask  
  14.         Future<?> f = group.submit(new Runnable() {  
  15.               
  16.             @Override  
  17.             public void run() {  
  18.                 System.out.println("任务正在执行");  
  19.                 //模拟耗时操作,比如IO操作  
  20.                 try {  
  21.                     Thread.sleep(1000);  
  22.                 } catch (InterruptedException e) {  
  23.                     e.printStackTrace();  
  24.                 }  
  25.                 System.out.println("任务执行完毕");  
  26.             }  
  27.         });  
  28.         //增加监听  
  29.         f.addListener( new FutureListener() {  
  30.             @Override  
  31.             public void operationComplete(Future arg0) throws Exception {  
  32.                 System.out.println("ok!!!");  
  33.             }  
  34.         });  
  35.         System.out.println("main thread is running.");  
  36.     }  
  37. }  

3.直到jdk1.8才算真正支持了异步操作,其中借鉴了某些框架的实现思想,但又有新的功能,同时在jdk1.8中提供了lambda表达式,使得java向函数式语言又靠近了一步。借助jdk原生的CompletableFuture可以实现异步的操作,同时结合lambada表达式大大简化了代码量。代码例子如下:

  1. /** 
  2.  * 基于jdk1.8实现任务异步处理 
  3.  *  
  4.  * @author Administrator 
  5.  * 
  6.  */  
  7. public class JavaPromise {  
  8.     public static void main(String[] args) throws Throwable, ExecutionException {  
  9.         // 两个线程的线程池  
  10.         ExecutorService executor = Executors.newFixedThreadPool(2);  
  11.         //jdk1.8之前的实现方式  
  12.         CompletableFuture<String> future = CompletableFuture.supplyAsync(new Supplier<String>() {  
  13.             @Override  
  14.             public String get() {  
  15.                 System.out.println("task started!");  
  16.                 try {  
  17.                     //模拟耗时操作  
  18.                     Thread.sleep(2000);  
  19.                 } catch (InterruptedException e) {  
  20.                     e.printStackTrace();  
  21.                 }  
  22.                 return "task finished!";  
  23.             }  
  24.         }, executor);  
  25.   
  26.         //采用lambada的实现方式  
  27.         future.thenAccept(e -> System.out.println(e + " ok"));  
  28.           
  29.         System.out.println("main thread is running");  
  30.     }  
  31. }  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值