使用单个更新程序线程的Java多线程缓存

时间:2010-09-29 23:47:52

标签: java multithreading ehcache

我有一个Web服务,平均有~1k个请求线程同时运行。这些线程从缓存(当前在ehcache上)访问数据。当缓存中的条目到期时,命中过期条目的线程尝试从DB获取新值,而其他线程也尝试命中此条目块,即我使用BlockingEhCache装饰器。我希望其他线程使用与“遗漏”键对应的“陈旧”值,而不是让其他线程等待“获取线程”。是否有任何第三方为此目的开发了ehcache装饰器?您知道有任何其他具有此行为的缓存解决方案吗?其他建议?

2 个答案:

答案 0 :(得分:1)

我不知道EHCache是​​否足以提供解决问题的具体建议,因此我将概述在没有EHCache的情况下我会做什么。

假设所有线程都使用名为FooService的Service接口和名为SimpleFooService的服务bean访问此缓存。该服务将具有获取所需数据所需的方法(也被缓存)。这样你就可以隐藏它从前端缓存的事实(http请求对象)。

我们不是简单地将要缓存的数据存储在服务的属性中,而是为它创建一个特殊对象。我们称之为FooCacheManager。它会将缓存存储在FooCacheManger的一个属性中(比如它的Map类型)。它将有getter来获取缓存。它还有一个名为reload()的特殊方法,它将从DB加载数据(通过调用服务方法来获取数据,或通过DAO),并替换缓存的内容(保存在属性中)

这里的诀窍如下:

  1. 将FooCacheManger中的缓存属性声明为AtomicReference(在Java 1.5中声明的新对象)。这可以在您阅读并分配线程时保证线程安全。您的读/写操作永远不会发生冲突,或读取半值写入。
  2. reload()将首先将数据加载到临时地图中,然后在完成时将新地图分配给保存在FooCacheManager中的属性。由于属性是AtomicReference,因此赋值是原子的,因此它基本上是在瞬间刷地图而无需锁定。
  3. TTL实现 - 让FooCacheManager实现QuartzJob接口,并使其成为一个石英作业。在作业的execute方法中,让它运行reload()。在Spring XML中,定义此作业每隔xx分钟运行一次(您的TTL),如果使用PropertyPlaceHolderConfigurer,也可以在属性文件中定义。
  4. 此方法自读取线程有效:

    1. 请勿阻止阅读
    2. 每次读取时都不要调用isExpired(),即1k /秒。
    3. 写入线程在写入数据时也不会阻塞。

      如果不清楚,我可以添加示例代码。

答案 1 :(得分:0)

由于ehcache删除陈旧数据,因此不同的方法可以刷新数据的概率随着到期时间的增加而增加,如果到期时间“足够”,则为0。

因此,如果线程1需要一些数据元素,它可能会刷新它,即使数据还不老。 在此期间,线程2需要相同的数据, 可能 使用现有数据(刷新线程尚未完成)。线程2可能也会尝试刷新。

如果您正在使用引用(更新程序线程加载对象,然后只是更改缓存中的引用),则缓存上的get和set操作不需要单独的同步。