20

在过去的几周里,我断断续续地尝试使用番石榴的MapMaker找到我理想的缓存实现。请在此处此处查看我之前的两个问题,以了解我的思考过程。

根据我所学到的,我的下一次尝试是放弃软值,转而使用 maximumSize 和 expireAfterAccess:

ConcurrentMap<String, MyObject> cache = new MapMaker()
        .maximumSize(MAXIMUM_SIZE)
        .expireAfterAccess(MINUTES_TO_EXPIRY, TimeUnit.MINUTES)
        .makeComputingMap(loadFunction);

在哪里

Function<String, MyObject> loadFunction = new Function<String, MyObject>() {
   @Override
   public MyObject apply(String uidKey) {
      return getFromDataBase(uidKey);
   }
};

但是,我仍然在努力解决的一个剩余问题是,一旦对象的时间到了,即使对象是可强烈访问的,此实现也会驱逐对象。这可能会导致具有相同 UID 的多个对象在环境中浮动,这是我不希望的(我相信我想要实现的目标被称为规范化)。

所以据我所知,唯一的答案是有一个额外的地图,它作为一个内部人,我可以检查一个数据对象是否仍在内存中:

ConcurrentMap<String, MyObject> interner = new MapMaker()
        .weakValues()
        .makeMap();

并且加载函数将被修改:

Function<String, MyObject> loadFunction = new Function<String, MyObject>() {
   @Override
   public MyObject apply(String uidKey) {
      MyObject dataObject = interner.get(uidKey);
      if (dataObject == null) {
         dataObject = getFromDataBase(uidKey);
         interner.put(uidKey, dataObject);
      }
      return dataObject;
   }
};

但是,使用两个映射而不是一个映射来缓存似乎效率低下。有没有更复杂的方法来解决这个问题?一般来说,我是否以正确的方式进行此操作,还是应该重新考虑我的缓存策略?

4

2 回答 2

8

两个映射是否有效完全取决于 getFromDatabase() 的成本以及对象的大小。做这样的事情似乎并没有超出所有合理的界限。

至于实现,看起来您可能可以以稍微不同的方式对地图进行分层以获得您想要的行为,并且仍然具有良好的并发属性。

  1. 创建您的第一个具有弱值的地图,并将计算函数 getFromDatabase() 放在此地图上。
  2. 第二张地图是即将到期的地图,也在计算,但这个函数只是从第一张地图中获取的。

通过第二张地图进行所有访问。

换句话说,过期映射将最近使用的对象子集固定在内存中,而弱引用映射是真正的缓存。

-dg

于 2011-07-25T06:39:54.203 回答
0

我不明白这里的全貌,但有两件事。

  1. 鉴于此声明:“此实现将驱逐对象,即使它们是强可达的,一旦它们的时间到了。这可能导致具有相同 UID 的多个对象在环境中浮动,这是我不想要的。” - 听起来你只需要使用 weakKeys() 而不是使用定时或基于大小的驱逐。

  2. 或者,如果您确实想在其中引入“内部人员”,我会使用真正的Interners.newWeakInterner.

于 2011-07-25T19:40:37.350 回答