我是java缓存的新手,我尝试了解按值存储与按引用存储之间的区别。
我在java.cache文档中有下面引用的段落 " 复制条目存储在缓存中以及从缓存返回时复制条目的目的是允许应用程序继续改变键和值的状态,而不会对缓存所持的条目造成副作用。 "
什么是"副作用"上文提到的?我们如何选择如何在实践中存储?
答案 0 :(得分:4)
问题很好,因为答案并不容易。实际语义在缓存实现中略有不同。
以参考方式存储:
缓存存储并返回相同的对象引用。
Object key = ...
Object value = ...
cache.put(key, value);
assert cache.get(key) == value;
assert cache.iterator().next().getKey() == key;
如果在存储值后改变密钥,则会出现模糊不清的情况。这与使用HashMap
或ConcurrentHashMap
。
使用以参考存储,以:
按值存储:
同样显而易见的是,按值存储确实意味着什么并不是那么清楚。根据JCache的规范主要内容:Brian Oliver表示它可以防止缓存数据损坏,Greg Luck说它是一切,但不是以引用方式存储。
就此而言,我确实分析了不同的兼容(通过TCK的方式)JCache实现。传递给缓存时会复制键和值对象,但是不能依赖于在返回应用程序时复制缓存中的对象这一事实。
因此,对于所有JCache实现,这种假设都不正确:
assert cache.get(key) != cache.get(key);
JCache实现在详细介绍时甚至可能会有更多变化。一个例子:
Map map = cache.getAll(...);
assert map.get(key) != map.get(key);
这是预期语义中的矛盾。我们希望地图内容是稳定的,OTOH缓存需要在每次访问时返回值的副本。 JCache规范没有为此强制执行具体语义。魔鬼在细节中。
由于每次缓存实现都会在存储时复制密钥,因此您可以获得缓存内部数据结构理智的额外安全性,但由于共享值引用,应用程序仍有机会中断。
我的个人结论(我愿意讨论):
由于按引用存储是一个可选的JCache功能,请求它,这意味着您将限制应用程序使用的缓存实现的数量。如果您不依赖按引用存储语义,请始终使用按值存储。
但是,不要让您的应用程序依赖于您认为可能通过按值存储获得的语义。在将其引用传递给缓存之后或从缓存中检索其引用之后,切勿改变任何对象。
如果仍有疑问,请询问您的缓存供应商。恕我直言,它是记录实施细节的良好做法。一个很好的例子(因为我花了很多时间考虑过......)是JCache chapter in the cache2k user guide
答案 1 :(得分:3)
防止可变对象的并发修改。副作用是使用该对象的其他线程。
例如,如果您的银行程序具有多个线程,其中一个Integer对象缓存表示它们之间共享的银行帐号。假设线程1从缓存中检索一个数字,然后开始对其执行操作。当线程1被操纵时,对象线程2检索相同的对象,并且也开始操纵它。由于它们以不协调的方式同时操纵同一个对象,因此结果是不可预测的。对象本身甚至可能被破坏。
按值存储消除了并发编程中的这个常见问题,如果它只是在将对象保存到缓存时存储对象的副本,并在从缓存中检索对象时分发对象的副本。