如何同步缓存列表访问
Posted
技术标签:
【中文标题】如何同步缓存列表访问【英文标题】:How do I synchronize cache list access 【发布时间】:2014-03-27 19:35:25 【问题描述】:我遇到了以下问题(一个重要的限制 - 不能使用外部 jar/库,只能使用常规安装附带的 java 原语):
X 类对象长期存储在 sql DB 中。出于性能考虑,对象被缓存(需要编写。打算基于 LinkedHashMap)。
get(key)
:
检查对象是否在缓存中且未使用 - 将其返回。
如果对象正在使用 - 睡眠直到它可用。
如果对象不在缓存中 - 从数据库中读取它。
putInCache(object)
:
更新缓存中的对象(如果它不存在,添加它)。
如果缓存耗尽,它将触发缓存的saveToDB
操作并删除
从缓存最近最少使用的项目。
saveToDB(object)
:
将对象写入数据库(未从缓存中删除)并标记对象并“未更改”。
有多个线程调用get
。线程可以更改它从get
接收到的对象(并且该对象将被标记为“已更改”) - 完成后它将调用putInCache
。
有一个专用线程遍历缓存对象,当它遇到“更改”的对象时,它将触发saveToDB
(在进行数据库访问时,对象将被标记为已使用)。
您建议如何确保线程安全?
基本上,我正在寻找能够启用的正确 Java 类:
1. get
同步它对缓存中每个对象的访问。这样它就可以检查它是否存在以及是否存在 - 它是否已被使用或免费抓取。如果它被使用 - 它应该休眠直到它可用。
2. 专用线程在调用saveToDB
时不应锁定缓存,但仍确保检查所有缓存并且不会导致饥饿(saveToDB
运行时缓存可能会更改)
只是为了澄清我只对锁定/同步解决方案感兴趣 - 可以假设缓存触发和数据库访问等事情是给定的。
【问题讨论】:
我将从使用synchronized
开始,您似乎不需要持有这个锁来执行saveToDB。当您想要的钥匙正在使用中时,您可以使用锁定wait()
。
@PeterLawrey:您能详细说明一下吗?你的意思是用同步包装所有功能吗?它将如何解决我的问题?我是 Java 新手,所以我可能缺少基本和明显的东西......
当您使用同步包装所有方法时,它们都隐式共享一个锁,因此您的缓存一次只能由一个线程访问。这意味着如果您可以避免包装任何可能是个好主意的方法,尤其是如果这些方法中的任何一个访问网络或任何非常慢的东西。 ;) 稍后您可以查看更复杂的锁定/线程安全策略,但我会先掌握使用同步。
我认为同步是一个巨大的互斥体(来自 C 世界)。我不想使用它——特别是在“专用线程”中,因为将所有更改的对象保存到数据库需要一些时间。这就是为什么我正在寻找一种更量身定制的方法。
也许是这样,但至少它可以正常工作(tm)。首先让您的代码使用它,然后使用更细粒度的锁定。
【参考方案1】:
这是一种方法:
使用ExecutorService
处理数据库请求;
使用Future
s 作为您的地图值;
使用ConcurrentHashMap
作为地图实现。
Future
应该从数据库中获取;它将使用ExecutorService
。
当您需要对一个对象进行操作时,请在此未来的.get()
上进行同步,这将是该对象。
另外,谷歌搜索“Java 并发实践”,并购买这本书;)
【讨论】:
感谢您的建议。我会去阅读你提到的课程。而且我确实有一本书,我在发布时正在阅读(“在 JVM 上编程并发”)。 注意:当我的意思是Future
我真的是指FutureTask
这里。它同时实现了Future
和Callable
。以上是关于如何同步缓存列表访问的主要内容,如果未能解决你的问题,请参考以下文章