最近碰到的异步处理的操作比较多,异步就是不等想要的结果返回执行接下来的其他操作,等异步结果返回后直接调用已经注册好的处理方法完成后续操作。异步的思想是非常棒的,相比轮询的方式而言,异步的实现方式无疑是高效并且优雅的。本文介绍了包括Future,AIO和有点类似于单机版的Map-Reduce的fork/join框架。
Guava ListenableFuture
使用JDK提供的线程池ExcuteService的execute(Runable runable)方法来执行不需要返回结果的线程任务,而使用submit(Callable callable)方法需要线程任务返回T类型的执行结果,方法返回Future对象,使用Future的get方法可以获取执行结果,而在执行get方法在线程返回结果之前是阻塞的,jdk这对于想要异步的处理结果没有提供相应的接口,guava的ListenableFuture接口就是为实现异步的获取Future中的结果而出现的。
顾名思义,ListenableFuture是可监听的Future,可以在结果返回的时候以方法回调的方式实现异步的后续操作。那么如何获取ListenableFuture呢,方法有两种:
1. 将jdk提供的Futhure转换成ListenableFuture
2. 将ExcutorService线程池转换成ListeningExcutorService,继而获取ListenableFuture
第一种方式我们使用如下适配器获得Future对应的ListenableFuture
ListenableFuture<String> listenableFuture=JdkFutureAdapters.listenInPoolThread(future);
第二种方式我们使用一个线程池的修饰类获得
ListeningExecutorService listeningThreadPool=MoreExecutors.listeningDecorator(threadPool);
ListenableFuture<String> listenableFuture=listeningThreadPool.submit(new Callable<String>() {....}
在获取了ListenableFuture之后,我们同样有两种方式异步获取线程执行结果
1. 添加FutureCallback执行回调方法
2. 为ListenableFuture添加监听线程
第一种方法使用Futures的addCallback方法实现
Futures.addCallback(listenableFuture,new FutureCallback<String>(){
@Override
public void onSuccess(String result) {
System.out.println(result);
}
@Override
public void onFailure(Throwable t) {
t.printStackTrace();
}
});
第二种方法不太推荐,使用listenableFuture.addListener的方法实现。
源码解读
我们看到guava的实现方式非常优雅,那么它是怎么实现这种异步回调的呢,以JdkFutureAdapters.listenInPoolThread为例,其实他是返回了一个ListenableFutureAdapter的内部类,它实现了ListenableFuture并且继承了ForwardingFuture类,然后调用callback的时候会新建一个Runable线程任务,其主要逻辑是使用Future的get方法阻塞获取执行结果,在结果完成的时候回到callback类的onSuccess方法,如果出现异常则调用onFailure方法。
整个方法的设计使用了适配器模式,ListenableFuture是最终用户需要的接口,ListenableFutureAdapter是适配器,ForwordingFuture实现了Future接口,是被适配者。
关于FutureTask
FutureTask是Future的一个实现类,它同时实现了Runable接口,直接使用线程池运行FutureTask,并且获取阻塞获取结果,其在异步方面并没有做出改变。
本节代码
所有测试代码获取可以点击这里
参考文献:guava并发,深入学习 FutureTask
Java AIO中的Future和CompleteHandler
Socket通信是Java网络通信的一种方式,在基础的阻塞BIO后出现了NIO,NIO采用了多路复用思想,使得一个线程可以监听多个socket文件描述符,使得在在一次轮训的过程中可以查看多个阻塞IO的状态,相比BIO每次只能监听一个IO状态