2010-09-29 69 views
4

我有一个具有约1k个请求线程的平均同时运行的Web服务。这些线程从缓存中访问数据(当前在ehcache上)。当缓存中的条目到期时,命中过期条目的线程尝试从数据库获取新值,而其他线程也试图访问该入口块,即我使用BlockingEhCache装饰器。我不希望其他线程在“获取线程”上等待,我希望其他线程使用与“未命中”键相对应的“陈旧”值。有没有第三方为此目的开发ehcache装饰器?你知道有这种行为的其他缓存解决方案吗?其他建议?具有单个更新程序线程的Java多线程缓存

+0

当缓存中的条目到期时会发生什么?他们是从缓存中删除,还是只是标记? – mlschechter 2010-09-30 01:24:14

+0

好主意。如果元素被标记为过期,则返回该值,然后启动刷新。 – 2010-09-30 02:09:54

+0

加载全新且未使用的缓存的另一种选择,然后将其与旧缓存交换。 – 2010-09-30 02:10:44

回答

1

我不知道EHCache足够好给出具体的建议来解决您的问题,所以我会概述我会做什么,没有EHCache。

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

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

的伎俩在这里是如下:

  1. 声明缓存财产FooCacheManger为AtomicReference(Java 1.5中的新申报对象)。这可以确保在读取并分配给它时的线程安全。您的读/写操作永远不会发生冲突,或者读取半写值。
  2. reload()将首先将数据加载到一个临时映射中,然后当它完成时它会将新映射分配给保存在FooCacheManager中的属性。由于该属性是AtomicReference,因此该赋值是原子的,因此它基本上是在不需要锁定的情况下瞬间刷新地图。
  3. TTL实现 - 让FooCacheManager实现QuartzJob接口,并使其实际成为石英工作。在作业的执行方法中,让它运行reload()。在Spring XML中,定义这个作业是为了每运行xx分钟(你的TTL),如果你使用PropertyPlaceHolderConfigurer,它也可以在属性文件中定义。

这种方法是有效的,因为读出线程:

  1. 不要阻塞用于读
  2. 不要称为isExpired()上的每个读出,这是1K /秒。

此外,写入线程在写入数据时不会阻塞。

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

0

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

所以,如果线程1需要一些数据元素,它可能会刷新它,即使数据还不旧。 与此同时,线程2需要相同的数据,它使用现有数据(虽然刷新线程尚未完成),但它可能会使用现有数据可能。线程2可能会尝试刷新。

如果您正在使用引用(updater线程加载对象,然后简单地更改高速缓存中的引用),那么对高速缓存执行get和set操作不需要单独的同步。