Latke CodeView - 1 cache

本贴最后更新于 3082 天前,其中的信息可能已经事过景迁

未注明的引用均为 Java 内部包
所有 setter、getter、logger 等逻辑无关代码均忽略不计
忽略了一些常见注解

org.b3log.latke.cache.local.memory.AbstractMemoryCache

import org.b3log.latke.cache.Cache; public abstract class AbstractMemoryCache<K extends Serializable, V extends Serializable> implements Cache<K, V> { private long maxCount = Long.MAX_VALUE; private long hitCount; private long missCount; private long putCount; private long cachedCount; protected final void hitCountInc() { hitCount++; } protected final void missCountInc() { missCount++; } protected final void putCountInc() { putCount++; } protected final void cachedCountInc() { cachedCount++; } protected final void cachedCountDec() { cachedCount--; } public long getCachedBytes() { // TODO: getCachedBytes return -1; } public long getHitBytes() { // TODO: getHitBytes return -1; } }
  • 抽象类实现接口的作用是什么?
  • 为什么采用 long 类型,int 竟然不够用?
  • 类声明处 K、V 的 extends 是什么意思

org.b3log.latke.cache.local.memory.LruMemoryCache

import org.b3log.latke.cache.local.util.DoubleLinkedMap; import org.b3log.latke.logging.Level; import org.b3log.latke.logging.Logger; import org.b3log.latke.util.Serializer; public final class LruMemoryCache<K extends Serializable, V extends Serializable> extends AbstractMemoryCache<K, V> implements Serializable { private DoubleLinkedMap<K, byte[]> map; public LruMemoryCache() { map = new DoubleLinkedMap<K, byte[]>(); } public void put(final K key, final V value) { //删除掉原有的,并添加到首部,即每次都将使用的放到最前面,是为LRU算法 remove(key); putCountInc(); synchronized (this) { if (getCachedCount() >= getMaxCount()) { collect(); } try { map.addFirst(key, Serializer.serialize((Serializable) value)); } catch (final IOException e) { return; } cachedCountInc(); } } public void putAsync(final K key, final V value) { put(key, value); } public synchronized V get(final K key) { final byte[] bytes = map.get(key); if (bytes != null) { hitCountInc(); map.makeFirst(key); try { return (V) Serializer.deserialize(bytes); } catch (final Exception e) { return null; } } missCountInc(); return null; } public synchronized void remove(final K key) { final boolean removed = map.remove(key); if (removed) { cachedCountDec(); } } public synchronized void remove(final Collection<K> keys) { for (final K key : keys) { remove(key); } } public synchronized void collect() { map.removeLast(); cachedCountDec(); } public synchronized void removeAll() { map.removeAll(); setCachedCount(0); setMissCount(0); setHitCount(0); } public boolean contains(final K key) { return null != get(key); // XXX: performance issue } public long inc(final K key, final long delta) { V ret = get(key); if (null == ret || !(ret instanceof Long)) { final Long v = delta; ret = (V) v; put(key, ret); } if (ret instanceof Long) { final Long v = (Long) ret + delta; ret = (V) v; put(key, ret); } return (Long) ret; } }
  • putCount 和 cachedCount 是什么关系?我以为 put 就是当前存放的容量,但是看代码使用 cachedCount 和 maxCount 相比的?
    读 Cache 接口发现注释:putCount 是指存放次数,cachedCount 是指当前存放的数量。
  • put 和 putAsync 看起来没什么区别,为何多加一个 putAsync?
  • inc 方法是什么意思?作用是什么?

org.b3log.latke.cache.local.util.DoubleLinkedMap

public final class DoubleLinkedMap<K, V> implements Serializable { private static final long serialVersionUID = 1L; private int size = 0; private DoubleLinkedMapNode<K, V> first; private DoubleLinkedMapNode<K, V> last; public synchronized boolean remove(final K key) { final DoubleLinkedMapNode<K, V> node = getNode(key); if (node != null) { removeNode(node); return true; } return false; } public synchronized V get(final K key) { if (first == null) { return null; } else { DoubleLinkedMapNode<K, V> current = first; while (current != null) { if (current.getKey().equals(key)) { return current.getValue(); } else { current = current.getNext(); } } } return null; } public synchronized void addLast(final K key, final V value) { final DoubleLinkedMapNode<K, V> node = new DoubleLinkedMapNode<K, V>(key, value); addLastNode(node); } public synchronized void addFirst(final K key, final V value) { if (null == key) { throw new IllegalArgumentException("Key is null!"); } final DoubleLinkedMapNode<K, V> node = new DoubleLinkedMapNode<K, V>(key, value); addFirstNode(node); } public synchronized void makeFirst(final K key) { final DoubleLinkedMapNode<K, V> node = getNode(key); if (node.getPrev() == null) { return; } //将当前节点的前一节点的下一节点改为当前节点的下一节点 node.getPrev().setNext(node.getNext()); if (node.getNext() == null) { //如果当前节点是最后一个节点,则将前一节点的下一节点设置为null last = node.getPrev(); last.setNext(null); } else { node.getNext().setPrev(node.getPrev()); } first.setPrev(node); node.setNext(first); node.setPrev(null); first = node; } public synchronized void removeAll() { //此处似乎直观的体现了Java不用做垃圾处理,如果是C/C++,应有回收空间代码 for (DoubleLinkedMapNode<K, V> me = first; me != null;) { if (me.getPrev() != null) { me.setPrev(null); } final DoubleLinkedMapNode<K, V> next = me.getNext(); me = next; } first = null; last = null; size = 0; } public synchronized V removeLast() { final DoubleLinkedMapNode<K, V> lastNode = removeLastNode(); if (null != lastNode) { return lastNode.getValue(); } return null; } public synchronized int size() { return size; } private synchronized DoubleLinkedMapNode<K, V> removeLastNode() { final DoubleLinkedMapNode<K, V> ret = last; if (last != null) { removeNode(last); } return ret; } private synchronized DoubleLinkedMapNode<K, V> getNode(final K key) { //遍历 if (first == null) { return null; } else { DoubleLinkedMapNode<K, V> current = first; while (current != null) { if (current.getKey().equals(key)) { return current; } else { current = current.getNext(); } } } return null; } private synchronized void removeNode(final DoubleLinkedMapNode<K, V> node) { if (node.getNext() == null) { if (node.getPrev() == null) { //删除双链表的唯一节点 if (node == first && node == last) { first = null; last = null; } } else { //删除尾节点 last = node.getPrev(); last.setNext(null); node.setPrev(null); } } else if (node.getPrev() == null) { //删除头节点 first = node.getNext(); first.setPrev(null); node.setNext(null); } else { //删除中间的节点 node.getPrev().setNext(node.getNext()); node.getNext().setPrev(node.getPrev()); node.setPrev(null); node.setNext(null); } size--; } private synchronized void addLastNode(final DoubleLinkedMapNode<K, V> node) { if (first == null) { first = node; } else { last.setNext(node); node.setPrev(last); } last = node; size++; } private synchronized void addFirstNode(final DoubleLinkedMapNode<K, V> node) { if (last == null) { last = node; } else { first.setPrev(node); node.setNext(first); } first = node; size++; } } final class DoubleLinkedMapNode<K, V> implements Serializable { private static final long serialVersionUID = -5617593667027497669L; private V value; private K key; private DoubleLinkedMapNode<K, V> prev; private DoubleLinkedMapNode<K, V> next; DoubleLinkedMapNode(final K key, final V value) { this.key = key; this.value = value; } public K getKey() { return key; } public V getValue() { return value; } protected DoubleLinkedMapNode<K, V> getNext() { return next; } protected void setNext(final DoubleLinkedMapNode<K, V> next) { //传递对象其实就是传递引用,如此说来与指针颇为类似 this.next = next; } protected DoubleLinkedMapNode<K, V> getPrev() { return prev; } protected void setPrev(final DoubleLinkedMapNode<K, V> prev) { this.prev = prev; } }
  • 为什么 addFirst 里判断了 key 是否为 null,而 addLast 没有?

node.getPrev().setNext(node.getNext());
if (node.getNext() == null) {
last = node.getPrev();
last.setNext(null);
} else {
node.getNext().setPrev(node.getPrev());
}

**这个逻辑看起来有点重复,是不是可以改为:** ```java node.getPrev().setNext(node.getNext()); if (node.getNext() != null) { node.getNext().setPrev(node.getPrev()); }
  • removeLast 和 removeLastNode 看起来也没什么区别,用意何在?

org.b3log.latke.cache.Cache

//接口定义,在AbstractMemoryCache中均已涉及 public interface Cache<K extends Serializable, V extends Serializable> { boolean contains(final K key); void put(final K key, final V value); void putAsync(final K key, final V value); V get(final K key); long inc(final K key, final long delta); void remove(final K key); void remove(final Collection<K> keys); void removeAll(); void setMaxCount(final long maxCount); long getMaxCount(); long getHitCount(); long getMissCount(); long getPutCount(); long getCachedCount(); long getCachedBytes(); long getHitBytes(); void collect(); }

org.b3log.latke.cache.CacheFactory

import org.b3log.latke.Latkes; import org.b3log.latke.RuntimeEnv; public final class CacheFactory { private static final Map<String, Cache<String, ?>> CACHES = Collections.synchronizedMap(new HashMap<String, Cache<String, ?>>()); public static synchronized void removeAll() { RuntimeEnv runtime = Latkes.getRuntime("cache"); if (RuntimeEnv.LOCAL == Latkes.getRuntimeEnv()) { runtime = RuntimeEnv.LOCAL; } switch (runtime) { case LOCAL: for (final Map.Entry<String, Cache<String, ?>> entry : CACHES.entrySet()) { final Cache<String, ?> cache = entry.getValue(); cache.removeAll(); } break; default: throw new RuntimeException("Latke runs in the hell.... Please set the enviornment correctly"); } } public static synchronized Cache<String, ? extends Serializable> getCache(final String cacheName) { Cache<String, ?> ret = CACHES.get(cacheName); try { if (null == ret) { switch (Latkes.getRuntime("cache")) { //通过反射获取到LruMemoryCache并得到一个实例 case LOCAL: final Class<Cache<String, ?>> localLruCache = (Class<Cache<String, ?>>) Class.forName( "org.b3log.latke.cache.local.memory.LruMemoryCache"); ret = localLruCache.newInstance(); break; default: throw new RuntimeException("Latke runs in the hell.... Please set the enviornment correctly"); } CACHES.put(cacheName, ret); } } catch (final Exception e) { throw new RuntimeException("Can not get cache: " + e.getMessage(), e); } return (Cache<String, Serializable>) ret; } private CacheFactory() { } }
  • Collections.synchronizedMap 看起来是个挺高端的玩意儿,有待 bing
  • 不太明白通过反射获取实例和直接 import 该类实例化有什么区别
  • 这个 cacheFactory 看起来是在 Latke 处调用的,有待观察其调用过程

org.b3log.latke.cache.NoCache

public final class NoCache<K extends Serializable, V extends Serializable> implements Cache<K, V> { private String name; public NoCache(final String name) { this.name = name; } public String getName() { return name; } public boolean contains(final K key) { return false; } } //methods implements from Cache
  • 该类继承了 Cache 接口并且所有方法均为空实现
  • 这个类的用意何在?
  • Latke

    Latke 是一款以 JSON 为主的 Java Web 框架。

    71 引用 • 535 回帖 • 826 关注
  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3196 引用 • 8215 回帖
  • 缓存
    42 引用 • 70 回帖
  • 双链表
    1 引用 • 2 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • ZephyrJung

    @88250 D 大,终于读完了 Cache 包下的代码,读代码真的是需要机缘,上次看一行没看下去,这次倒是通读了一遍,大部分还挺理解的
    文中列表加粗的行是有疑问的地方,还请 D 大不吝赐教 🙏

  • 88250

    org.b3log.latke.cache.local.memory.AbstractMemoryCache

    • 抽象类实现接口的作用是什么?
      可能还有非内存型的实现。
    • 为什么采用 long 类型,int 竟然不够用?
      保险起见。
    • 类声明处 K、V 的 extends 是什么意思
      限定了 key、value 的类型必须派生自 Serializable

    org.b3log.latke.cache.local.memory.LruMemoryCache

    • put 和 putAsync 看起来没什么区别,为何多加一个 putAsync?
      本来想做一套异步接口的,后来没实现。
    • inc 方法是什么意思?作用是什么?
      强制把 value 作为 long 值并自增 delta 并返回。

    org.b3log.latke.cache.local.util.DoubleLinkedMap

    • 为什么 addFirst 里判断了 key 是否为 null,而 addLast 没有?
      看样子是漏了。
    • 这个逻辑看起来有点重复,是不是可以改为:
      这样的话 last 重置不了。
    • removeLast 和 removeLastNode 看起来也没什么区别,用意何在?
      返回值不一样,抽象层面不一样,提供给不同的场景使用/复用。

    org.b3log.latke.cache.CacheFactory

    • 不太明白通过反射获取实例和直接 import 该类实例化有什么区别
      cache 包因为实现和接口都在同一个 jar 里面,所以的确是可以像你说的直接 new 的,这里这样写是为了和其他服务(比如 repository)构造时保持相同的编程范式。

    org.b3log.latke.cache.NoCache

    • 这个类的用意何在?
      为了方便替换已经用了真正 cache 的地方做穿透测试?不大记得了..