【发布时间】:2012-04-24 19:40:42
【问题描述】:
在使用 CMT 的 Java EE 服务器上,我使用 ehcache 在业务对象层 (EJB) 和数据访问层(使用 JDBC 的 POJO)之间实现缓存层。在使用自填充 Ehcache 时,我似乎遇到了访问同一记录的两个线程之间的竞争条件。缓存以记录的主键为键。
场景是:
- 第一个线程更新数据库中的记录并从缓存中删除记录(但数据库提交不一定立即发生 - 可能还有其他查询要遵循。)
- 第二个线程读取记录,导致重新填充缓存。
- 第一个线程提交事务。
这一切都在几分之一秒内发生。这会导致缓存与数据库不同步,并且后续读取记录会返回陈旧的缓存数据,直到执行另一次更新,或者缓存中的条目过期。我可以处理短时间的陈旧数据(交易的典型长度),但不能处理几分钟,这是我希望缓存对象的时间。
对于避免这种竞争条件有什么建议吗?
更新:
在事务提交后清除缓存当然是理想的。问题是,在使用 CMT 的 J2EE 环境中,当缓存层夹在业务层(无状态会话 EJB)和数据访问层之间时,如何做到这一点?
要清楚这施加的约束,所讨论的方法调用可能与之前或之后发生的其他方法调用在同一事务中,也可能不在同一事务中。我不能强制提交(或在单独的事务中完成这项工作),因为这会改变客户端代码所期望的事务边界。任何后续异常都不会回滚整个事务(在这种情况下不必要地清除缓存是可接受的副作用)。我无法控制事务的入口点,因为它本质上是客户端可以使用的 API。将清除缓存的责任推给客户端应用程序是不合理的。
我希望能够推迟任何缓存清除操作,直到整个事务由 EJB 容器提交,但我没有发现任何方法可以挂钩该逻辑并使用无状态会话 bean 运行我自己的代码。
更新 #2:
目前最有希望的解决方案是使用 ehcache 2.0 的 JTA 支持,除了重大的设计更改:http://ehcache.org/documentation/apis/jta
这意味着升级到 ehcache 2.x 并为数据库启用 XA 事务,这可能会产生负面影响。但这似乎是“正确”的方式。
【问题讨论】:
标签: java jakarta-ee concurrency ehcache race-condition