对象池模式
对象池模式,也称对象池服务,通过循环使用对象,减少资源在初始化和释放时的昂贵损耗。
简单地说,在需要时,从池中提取;不用时,放回池中,等待下一个请求。典型例子是连接池和线程池。
public abstract class ObjectPool<T> {
//容器,容纳对象
private Hashtable<T, ObjectStatus> pool = new Hashtable<T, ObjectStatus>();
//初始化时创建对象,并放入到池中
public ObjectPool() {
pool.put(create(), new ObjectStatus());
}
//从Hashtable中取出空闲元素
public synchronized T checkOut() {
//这是最简单的策略
for (T t : pool.keySet()) {
if (pool.get(t).validate()) {
pool.get(t).setUsing();
return t;
}
}
return null;
}
//归还对象
public synchronized void checkIn(T t) {
pool.get(t).setFree();
}
class ObjectStatus {
//占用
public void setUsing() {
}
//释放
public void setFree() {
//注意:若T是有状态,则需要回归到初始化状态
}
//检查是否可用
public boolean validate() {
return false;
}
}
//创建池化对象
public abstract T create();
}
对象池模式的实际应用
有一个专业的新闻追踪网站,最近一段时间内出现偶发性缓慢,从监控情况上看,响应时间在2秒以上。网站首页内容基本都是静态的(轮询生成),唯一的动态部分是网站的激励语,如“积一时之跬步,臻千里之遥程”、“业精于勤,荒于嬉;行成于思,毁于随”等励志语句,这是一个简单的SQL随机查询结果,表中的数量在5000条左右,而且结构简单,查询性能不是问题。
//原语句
@Service
public class WisdomProvider {
@Autowire
private WisdomDao wisdomDao;
public String getOneWord() {
return wisdomDao.randomOneWisdom();
}
}
在跟踪过程中,发现高峰期数据库连接偶尔出现占满情况,而且都是查询该表(该数据库的随机查询算法有缺陷),问题找到了:每一次访问都会直接查询数据库,没有缓存。通常情况下,这没有问题,但是在高并发的情况下,例如在10万PV的压力下服务器基本就垮掉了,这是非常严重的问题。因此,引入一个对象池,把这5000条记录(根据评估最多不超过20000条记录)在启动时直接加载到内存中,在需要时再从内存中取得,以后查询不再与数据库交互。
@Service
public class WisdomProvider {
@Autowire
private WisdomDao wisdomDao;
private List<String> wisdoms = null;
@PostConstruct
public void init() {
wisdoms = wisdomDao.getAll();
}
public String getOneWord() {
return RandomUtils.getOne(wisdoms);
}
}
@PostConstruct注解的作用是Spring容器在启动完毕后,直接执行init方法,一次性读取所有的数据,然后在应用运行期间不再与数据库交互,直接从List列表中获取数据。完事儿后系统性能大幅提升,在不增加硬件的情况下,彻底解决了性能问题。