我正在使用类似的东西
Cache<Integer, Item> cache;
其中Item
彼此独立,看起来像
private static class Item {
private final int id;
... some mutable data
synchronized doSomething() {...}
synchronized doSomethingElse() {...}
}
这个想法是从缓存中获取项目并对其调用同步方法。万一错过了,可以重新创建该项目,这很好。
当一个项目从缓存中逐出并重新创建时,当线程运行同步方法时,就会出现问题。一个新线程获取一个新项目并对其进行同步...因此,对于单个 id,同步方法内有两个线程。失败。
有没有简单的方法可以解决这个问题?这是Guava Cache ,如果有帮助的话。
最佳答案
我认为Louis的建议,使用 key 来锁定是最简单实用的。这是代码片段,在没有 Guava 库的帮助下,说明了这个想法:
static locks[] = new Lock[ ... ];
static { /* initialize lock array */ }
int id;
void doSomething() {
final lock = locks[id % locks.length];
lock.lock();
try {
/* protected code */
} finally {
lock.unlock();
}
}
锁数组的大小限制了您获得的最大并行度。如果您的代码仅使用 CPU,您可以通过可用处理器的数量来初始化它,这是完美的解决方案。如果您的代码等待 I/O,您可能需要任意大的锁数组,或者限制可以运行关键部分的线程数量。在这种情况下,另一种方法可能会更好。
更概念层面的评论:
如果您想防止项目被逐出,您需要一种名为固定的机制。在内部,大多数缓存实现都使用它,例如用于在 I/O 操作期间阻塞。某些缓存可能会公开应用程序执行此操作的方法。
在JCache兼容的缓存中,有EntryProcessor的概念。 EntryProcessor 允许您以原子方式处理条目上的代码。这意味着缓存正在为您完成所有锁定。根据问题的范围,这可能有一个优势,因为这也适用于集群场景,这意味着锁定是集群范围的。
我想到的另一个想法是可否决驱逐。这是 EHCache 3 正在实现的概念。通过指定可否决的逐出策略,您可以自行实现固定机制。
关于java - 同步缓存项目,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34236241/