如何同步缓存列表访问

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 处理数据库请求; 使用Futures 作为您的地图值; 使用ConcurrentHashMap 作为地图实现。

Future 应该从数据库中获取;它将使用ExecutorService

当您需要对一个对象进行操作时,请在此未来的.get() 上进行同步,这将是该对象。

另外,谷歌搜索“Java 并发实践”,并购买这本书;)

【讨论】:

感谢您的建议。我会去阅读你提到的课程。而且我确实有一本书,我在发布时正在阅读(“在 JVM 上编程并发”)。 注意:当我的意思是Future 我真的是指FutureTask 这里。它同时实现了FutureCallable

以上是关于如何同步缓存列表访问的主要内容,如果未能解决你的问题,请参考以下文章

缓存同步如何保证缓存一致性缓存误用

讨论:缓存同步如何保证缓存一致性缓存误用

缓存同步如何保证缓存一致性缓存误用!

我是不是需要同步对仅由一个线程修改的列表的访问?

在Java中同步String对象

访问 List 项时 UI 和 Worker 线程同步