javax.cache 按引用存储与按值存储

Act*_*ton 3 java caching jcache

我是 Java 缓存的新手,我试图了解按值存储按引用存储之间的区别。

我在下面引用了 java.cache 文档中的段落“复制存储在缓存中的条目以及从缓存返回时再次复制条目的目的是允许应用程序继续改变键和值的状态而不会导致侧面 -对缓存所持有的条目的影响。”

上面提到的“副作用”是什么?而在实践中我们又该如何选择存储方式呢?

cru*_*tex 6

这个问题很好,因为答案并不容易。不同缓存实现的实际语义略有不同。

参考存储

缓存存储并返回相同的对象引用。

Object key = ...
Object value = ...
cache.put(key, value);
assert cache.get(key) == value;
assert cache.iterator().next().getKey() == key;
Run Code Online (Sandbox Code Playgroud)

如果在存储值后对键进行变异,则会出现模棱两可的情况。使用 aHashMap或时效果相同ConcurrentHashMap

使用store by reference来:

  • 最大化性能/最小化处理开销
  • 当数据适合 Java 堆时
  • 如果你想在存储后改变一个值。这对性能很有用,但不是推荐的做法,因为您必须处理并发问题,并且使用依赖于按引用语义存储

按值存储

同样显而易见的是,按价值存储的真正含义并不是很清楚。根据 JCache 的规范负责人:Brian Oliver 说它可以防止缓存数据损坏,Greg Luck 说它是一切,但不是通过引用存储

就此而言,我确实分析了不同的兼容(意味着通过 TCK)JCache 实现。键和值对象在传递到缓存时会被复制,但您不能依赖缓存中的对象在返回到应用程序时会被复制的事实。

所以这个假设并不适用于所有 JCache 实现:

assert cache.get(key) != cache.get(key);
Run Code Online (Sandbox Code Playgroud)

JCache 实现甚至可能有更多的变化,当它进入细节时。一个例子:

Map map = cache.getAll(...);
assert map.get(key) != map.get(key);
Run Code Online (Sandbox Code Playgroud)

这是预期语义中的矛盾。我们希望地图内容是稳定的,OTOH 缓存需要在每次访问时返回值的副本。JCache 规范没有为此强制执行具体的语义。细节决定成败。

由于每个缓存实现都在存储时复制键,因此您将获得额外的安全性,即缓存内部数据结构是健全的,但应用程序仍然有机会因共享值引用而中断。

我的个人结论(我愿意讨论):

由于按引用存储是一个可选的 JCache 功能,请求它意味着您限制了应用程序使用的缓存实现的数量。如果您不依赖按引用语义存储,则始终使用按值存储

但是,不要让您的应用程序依赖于您认为可以通过按值存储获得的语义。在将其引用传递给缓存或从缓存中检索其引用后,切勿更改任何对象。

如果仍有疑问,请询问您的缓存供应商。恕我直言,这是记录实施细节的良好做法。一个很好的例子(因为我花了很多心思......)是cache2k 用户指南中JCache 章节