Latke CodeView - 1 cache

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

未注明的引用均为 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 回帖 • 787 关注
  • Java

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

    3187 引用 • 8213 回帖
  • 缓存
    42 引用 • 70 回帖
  • 双链表
    1 引用 • 2 回帖

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • 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 的地方做穿透测试?不大记得了..
  • 其他回帖
  • ZephyrJung

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