4

背景资料

您可以使用 LinkedHashMap 制作 LRU 缓存,如此链接所示。基本上,您只需:

  • 扩展链接的哈希映射。
  • 提供容量参数。
  • 用参数初始化超类(LinkedHashMap),告诉它它的容量、缩放因子(永远不应该使用),并保持项目的插入/引用顺序。
  • 当容量被破坏时,覆盖 removeEldestEntry 以删除最旧的条目。

我的问题

这是一个非常标准的 LRU 缓存实现。但是我不知道该怎么做的一件事是当 LinkedHashMap 由于最近没有足够使用而删除一个条目时如何通知它。

我知道我可以让 removeEldestEntry 提供某种形式的通知......但是有没有办法在将新元素插入(放置)到底层地图时检索从缓存中删除的元素?或者,有没有办法查询从缓存中删除的最后一项?

4

2 回答 2

2

您可以通过创造性地使用线程本地存储来使其工作:

class LRUCacheLHM<K,V> extends LinkedHashMap<K,V> {

    private int capacity;

    public LRUCacheLHM(int capacity) {
        //1 extra element as add happens before remove (101), and load factor big
        //enough to avoid triggering resize.  True = keep in access order.
        super(capacity + 1, 1.1f, true);
        this.capacity = capacity;
    }
    private ThreadLocal<Map.Entry<K,V>> removed = new ThreadLocal<Map.Entry<K,V>>();
    private ThreadLocal<Boolean> report = new ThreadLocal<Boolean>();
    {
        report.set(false);
    }
    @Override
    public boolean removeEldestEntry(Map.Entry<K,V> eldest) {
        boolean res = size() > capacity;
        if (res && report.get()) {
            removed.set(eldest);
        }
        return res;
    }
    public Map.Entry<K,V> place(K k, V v) {
        report.set(true);
        put(k, v);
        try {
            return removed.get();
        } finally {
            removed.set(null);
            report.set(false);
        }
    }

}

演示。

该方法背后的想法是通过将线程本地标志设置place(K,V)为来removeEldestEntry表示我们希望获得最旧的条目。当看到这个标志并且知道一个条目正在被删除时,它会将最旧的条目放在变量中,这也是线程局部的。reporttrueremoveEldestEntryreport

调用removeEldestEntry发生在对put方法的调用内部。之后,最老的条目是null, 或 位于report变量中,准备好被收获。

调用set(null)对于removed避免挥之不去的内存泄漏很重要。

于 2015-04-29T14:57:15.990 回答
1

当新元素插入(放置)到基础地图中时,有什么方法可以检索从缓存中删除的元素?

removeEldestEntry通知要删除的条目。如果要使其动态可配置,可以添加此方法调用的侦听器。

来自 Javadoc

protected boolean removeEldestEntry(Map.Entry eldest)

eldest - 映射中最近最少插入的条目,或者如果这是一个按访问排序的映射,则为最近最少访问的条目。这是将被删除的条目,此方法返回 true。如果在 put 或 putAll 调用导致此调用之前映射为空,则这将是刚刚插入的条目;换句话说,如果映射包含单个条目,则最旧的条目也是最新的。

.

有没有办法查询从缓存中删除的最后一项?

删除的最后一项已被删除,但是您可以让子类将此条目存储在稍后可以检索的字段中。

于 2015-04-29T14:26:10.590 回答