在Java并发编程领域,FutureTask可以说是一个非常强大的利器,它通过实现RunnableFuture接口间接拥有了Runnable和Future接口的相关特性,既可以用于充当线程执行的任务(Runnable),也可以用于获取线程异步执行任务后返回的结果(Future);本文将基于FutureTask实战一个高级案例:设计一款简化版的池容器,以此学习巩固池化思想.
言归正传,在上篇文章中:Java并发编程(2)-FutureTask详解与池化思想的设计和实战一,我们已经从源码的角度结合多线程ThreadPoolExecuto,深入剖析并解读了FutureTask 的相关API,从任务的创建、到任务的执行 最后再到 线程执行完任务后异步获取执行结果;整个过程下来,想必各位看官老爷们应该收获颇丰。
而本文我们将趁热打铁,进一步介绍FutureTask在实际项目开发中的作用;总的来说,FutureTask在实际项目开发中起到的作用有两个:
(一)FutureTask执行多任务计算的场景
比如网站“程序员实战基地fightjava.com”的首页的数据是由多个功能模块组成的:轮播图、最新课程、最新博客、最新学习路线、最新资料、友情链接等模块数据;
这些模块由于具有独立性、互不相关性,因此可以开启多个FutureTask,然后交由多线程去执行,最终再统一通过get()方法获取多线程异步执行任务的结果返回给前端浏览器(这一场景在debug最新撸的课程:Java工程师核心技术-典型案例与面试实战系列二 就有重点介绍过,感兴趣的小伙伴可以前往观看学习!)
(二)在高并发场景下确保任务只被执行一次
在很多高并发的场景下,往往我们只需要某些任务被执行一次,这种情景FutureTask就能胜任(当然啦,可能还要借助像ConcurrentHashMap这样的组件辅助;而本文要介绍的便是在这一场景下FutureTask所发挥的作用!
按照惯例,我们还是先来介绍下这一场景/需求吧:假设有一个带Key的连接池,当Key存在时,则直接返回Key对应的连接对象;当Key不存在时,则创建一个 “连接”对象;
对于这样的应用场景,通常采用的方法是使用一个Map来存储Key和连接池对应的对应关系,而由于这是出于高并发的应用场景,因此稳妥的方式是采用ConcurrentHashMap,下面debug将采用N种方式对此进行实现,当然啦,最后的实现方式当然是FutureTask啦,毕竟大佬总是最后才出场的!!!
话不多说,进入代码实战环节
(1)首先出场的是传统的实现方式,即按照常规的业务逻辑、算法实现的方式:
@Component
@Slf4j
public class MyConnectionPool {
private ConcurrentHashMap<String,MyConnection> connMap=new ConcurrentHashMap<>();
//获取链接
public MyConnection getConn(final String key){
MyConnection conn=connMap.get(key);
if (conn!=null){
return conn;
}
conn=createConn(key);
connMap.putIfAbsent(key,conn);
return conn;
}
}
代码的含义应该不难理解哈,这但凡有点英语基础的闭着眼睛都能猜出来是啥意思,除非你英语是体育老师教的:
那么到底这种方式行不行呢?其实行不行并不是由你我说了算,而是得需要先经过压测哈,因为我们上面已经说了,必须要满足“高并发环境”的前提;OK,啪的一下打开JMeter.sh,然后设置QPS=1000,甚至10000 ,很快啊,如下图所示:
从上图中就可以看出,此种方式虽然最终是可以得到想要的结果,但是却产生了大量的、很有可能会被闲置的连接资源,因此这种方式不值得推荐!
(2)第二种要出场的是“synchronized”,其实现代码如下所示:
public synchronized MyConnection getConnA(final String key){
MyConnection conn=connMap.get(key);
if (conn!=null){
return conn;
}
conn=createConn(key);
connMap.putIfAbsent(key,conn);
return conn;
}
相对于第一种方式,虽然解决了安全性问题,但是却大大牺牲了性能,无法提高前端的并发量;即 synchronized这种同步方式,虽然牺牲了性能,但却没有浪费由于大量创建的连接所占用的空间资源(这其实是一种加 独占/悲观 锁的方式)
更多请见:http://www.mark-to-win.com/tutorial/51111.html