高并发、大访问量实际是程序猿界的分水岭,如何从各个角度去解决这些问题?这也是面试的时候架构师被问过的最多的问题。而异步、非阻塞对于性能的提升是每个武林高手的锦囊妙计。
异步和非阻塞是什么关系?什么区别?
此问题一出,可以直接鉴别真伪,不信你问下试试~哈哈!!!看完此文,相信你就清楚了。
异步毫无疑问,就是启动多个线程,让每个线程去做一部分工作,互不干扰。
当需要汇聚结果的时候,必须采用futrue的模式。简单来讲,就是当你用多线程异步的去执行的时候,如果不需要知道结果,可以直接结束。但是当你需要知道结果的时候,最好采用callable和futrue配合完成。例如,如果对用户表进行了水平切分,需要从多个表查询数据时,就可以采用这种方式。Futrue就是你需要得到的结果。它代表的是未来,因为是异步,future.get()不一定有结果,如果你去get的时候还没有结果,是会被阻塞的。但是在你必须获取结果之前,还可以做很多事情,这时候是不受影响的。是非阻塞的状态。
Future模式需要通过轮训或阻塞等待的方式,才能得到结果。这样总是显得不太优雅,比较好的方式应该是callback(回调函数,写过ajax的肯定比较清楚)的方式,也就是执行结束的时候异步通知完成状态。然后再去futrue中取执行结果。
实际上futrue、callback这种经典的模型在很多语言里都有了原生的支持,jdk中虽然有futrue,但是并不支持callback模式。还好guava给我们又打开了一扇窗——ListenableFuture。在jdk8中已经得到了完善。找个时间再写写。
异步、非阻塞给你带来的问题就是调试的麻烦,编程复杂度的提升,不过有了这些工具类,变得简单了很多。如果公司有开发框架的话,可以直接封装在底层。
直接上代码,可以仔细看下注释,你就明白整个过程了。
public class FutureExample {
public static void main(String[] args) throws Exception {
// jdk自带Future模式,实现异步,交给线程池处理任务
ExecutorService jdkExecutor =Executors.newSingleThreadExecutor();
Future<String> jdkFuture = jdkExecutor
.submit(new Callable<String>() {
@Override
public String call() throws Exception {
// 模拟业务消耗时间
TimeUnit.SECONDS.sleep(100);
return "This is native future call.not supportasync callback";
}
});
// Future只实现了异步,而没有实现回调.所以此时主线程get结果时阻塞.或者可以轮训以便获取异步调用是否完成,提交到线程池到get结果之间是非阻塞的,可以处理其他任务。
System.out.println(jdkFuture.get());
// 好的实现应该是提供回调,即异步调用完成后,可以直接回调.本例采用guava提供的异步回调接口,方便很多.
ListeningExecutorService guavaExecutor = MoreExecutors
.listeningDecorator(Executors.newSingleThreadExecutor());
final ListenableFuture<String>listenableFuture = guavaExecutor
.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);
return "this is guava future call.support asynccallback";
}
});
// 注册监听器,即异步调用完成时会在指定的线程池中执行注册的监听器
listenableFuture.addListener(new Runnable() {
@Override
public void run() {
try {
System.out.println("async complete.result:"+ listenableFuture.get());
} catch (Exception e) {
}
}
}, Executors.newSingleThreadExecutor());
// 主线程可以继续执行,异步完成后会执行注册的监听器任务.
System.out.println("go on execute.asyn complete willcallback");
// 除了ListenableFuture,guava还提供了FutureCallback接口,相对来说更加方便一些.
ListeningExecutorService guavaExecutor2 = MoreExecutors
.listeningDecorator(Executors.newSingleThreadExecutor());
final ListenableFuture<String>listenableFuture2 = guavaExecutor2
.submit(new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(1000);
System.out.println("asyncThreadName:"
+ Thread.currentThread().getName());
return "this is guava future call.support asynccallback using FutureCallback";
}
});
// 注意这里没用指定执行回调的线程池,从输出可以看出,默认是和执行异步操作的线程是同一个.当然也可以再起一个线程池,取决于处理的复杂度,和相互之间的影响。
Futures.addCallback(listenableFuture2, new FutureCallback<String>() {
@Override
public void onSuccess(String result) {
System.out
.println("async callback(using FutureCallback) result:"
+ result);
System.out.println("execute callback threadName:"
+ Thre }ad.currentThread().getName());
@Override
public void onFailure(Throwable t) {
}
});
}
}
参考文献:
http://www.blogjava.net/landon/archive/2014/02/27/410387.html(代码)
http://ifeve.com/google-guava-listenablefuture/
更多文章欢迎关注我的微信公众号,奔跑中的蜗牛,可以扫描头像关注