|
楼主 |
发表于 2003-4-20 11:14:12
|
显示全部楼层
底层数据结构
jive缓存机制的原理其实很简单,就是把所要缓存的对象加到HashMap哈希映射表中,用两个LinkedListedlist 双向链表分别维持着缓存对象和每个缓存对象的生命周期,如果一个缓存对象被访问到,那么就把它放到链 表的最前面,然后不定时的把要缓存对象的对象加入链表中,把过期对象删除,如此反复。我们比较一下第 一和第二部分就可以发现,他们的代码几乎完全相同。差别就在第二部分的哈希映射表没有采用JDK提供的 类,而是采用了作者自己编写的一个类,他将原来哈希映射表的Key的类型由Object改为Long,这样做虽然在 一定程度上加快了缓存的速度并减小了缓存的大小,但无形之中也减低了程序的稳定性和可读性,因此不推 荐大家仿效。值得一提的是,在Jive1.0.2版中,所有Forum,Thread,Message的ID和他们的内容的缓存都是用 第一部分的Java类实现的,在升级到后面的版本时,其内容采用了第二部分的Java类实现,但其ID仍用第一部 分的Java类实现,这也是Jive容易混淆的一个地方。好了,我们先来看第一部分的Java类实现。
我们先来看LinkedListNode类的源码:
public class LinkedListNode {
public LinkedListNode previous;
public LinkedListNode next;
public Object object;
public long timestamp;
public LinkedListNode(Object object, LinkedListNode next,
LinkedListNode previous)
{
this.object = object;
this.next = next;
this.previous = previous;
}
public void remove() {
previous.next = next;
next.previous = previous;
}
public String toString() {
return object.toString();
}
}
很明显,这是一个双向链表的节点类,previous,next分别记录前后节点的指针,object用于记录所需缓存的对 象,timestamp用于记录当前节点被创建时的时间戳。当该时间戳超过该节点的生存周期时,它就会被 remove()方法删除掉。就是由LinkedListNode构成了LinkedList链表,而LinkedList类中只是实现了 getFirst(),getLast(),addFirst(),addLast(),clear()等链表的基本方法,没有其他内容。
再来看Cacheable接口和它的一个实现类CacheableInt:
public interface Cacheable {
public int getSize();
}
public class CacheableInt implements Cacheable {
private int intValue;
public CacheableInt(int intValue) {
this.intValue = intValue;
}
public int getInt() {
return intValue;
}
public int getSize() {
return CacheSizes.sizeOfObject() + CacheSizes.sizeOfInt();
}
}
从上面的代码可以看到Cacheable接口只有一个方法getSize(),它要求继承类实现该方法汇报占用缓存的大 小,以便实施管理。Integer,Boolean,Long,LongArray,String类型都有其对应的类,CacheSizes类则把各 种类型的长度封装起来,便于修改和移植。那么为什么CacheableInt. getSize()得到的是 sizeOfObject()+sizeOfInt()呢,聪明的读者一定想到了,因为任何都继承自Object,计算空间时当然也要把它 给算上。
还有一个CacheObject类,它是缓存的基本元素,我们来看代码:
public final class CacheObject {
public Cacheable object;
public int size;
public LinkedListNode lastAccessedListNode;
public LinkedListNode ageListNode;
public CacheObject(Cacheable object, int size) {
this.object = object;
this.size = size;
}
}
lastAccessedListNode记录着一个缓存节点的Key,是构成lastAccessedList链表的基本元素,在 lastAccessedList链表中,经常被访问到的节点总是在最前面。而ageListNode记录着缓存节点的加入时间, 是构成ageList链表的基本元素,而ageList链表则是按时间先后排序,先加入的节点总是在最后 面。lastAccessedListNode和ageListNode本来可以写成两个类,毕竟lastAccessedListNode并不需要 ageListNode的成员变量timestamp,但是为了简化程序,Jive把他们写成了一个类,这也是容易混淆的一个地 方。
现在来看缓存机制中最关键的一个类Cache的部分代码,主要是add()和get()方法:
public class Cache implements Cacheable {
protected static long currentTime = CacheTimer.currentTime;
protected HashMap cachedObjectsHash;
protected LinkedList lastAccessedList;
protected LinkedList ageList;
//缓存元素的最大尺寸128kbit,可修改
protected int maxSize = 128 * 1024;
//整个缓存的大小
protected int size = 0;
//缓存元素的最大保存时间,用Cache(long maxLifetime)初始化
protected long maxLifetime = -1;
//记录cache的命中次数和未命中次数
protected long cacheHits, cacheMisses = 0L;
……
//向哈希表中添加一个关键字为Key的缓存对象object
public synchronized void add(Object key, Cacheable object) {
//先把原来的对象remove掉
remove(key);
int objectSize = object.getSize();
//如果对象太大,则不加入缓冲存
if (objectSize > maxSize * .90) {
return;
}
size += objectSize;
//新建一个缓存对象,并放入哈希表中
CacheObject cacheObject = new CacheObject(object, objectSize);
cachedObjectsHash.put(key, cacheObject);
// 把缓存元素的Key放到lastAccessed List链表的最前面
LinkedListNode lastAccessedNode = lastAccessedList.addFirst(key);
cacheObject.lastAccessedListNode = lastAccessedNode;
//把缓存元素的Key放到ageList链表的最前面,并记下当前时间
LinkedListNode ageNode = ageList.addFirst(key);
ageNode.timestamp = System.currentTimeMillis();
cacheObject.ageListNode = ageNode;
// 在cullCache()中,先调用deleteExpiredEntries()把过期对象删掉,如果缓存还是太满,则掉用 remove(lastAccessedList.getLast().object)把lastAccessedList中不常访问的对象删掉
cullCache();
}
//在哈希表中得到一个关键字为Key的缓存对象object
public synchronized Cacheable get(Object key) {
// 清理过期对象
deleteExpiredEntries();
CacheObject cacheObject = (CacheObject)cachedObjectsHash.get(key);
if (cacheObject == null) {
//没找到则未命中次数加一
cacheMisses++;
return null;
}
//找到则命中次数加一
cacheHits++;
//将该缓存对象从lastAccessedList链表中取下并插入到
//链表头部
cacheObject.lastAccessedListNode.remove();
lastAccessedList.addFirst(cacheObject.lastAccessedListNode);
return cacheObject.object;
}
到这里第一部分的Java类实现就说完了,正如上文提到的那样,第二部分的Java类实现与第一部分基本上没 有什么差别,因此就不再赘述。下面给出第二部分的类图,以供读者参考。 |
|