要求似乎是这样的:
- 有一个
Map<String, Object>
那是一个缓存。
- 池中有许多工作线程访问缓存
- 某些类型的工作需要在完成后使缓存中的对象失效
首先你需要一个ConcurrentHashMap<String, Lock> keys
. This Map
将存储之间的关系String
键和和Lock
我们将使用锁钥匙的对象。这使我们能够替换key -> value
无需锁定整个数据的映射Map
.
接下来你将需要一个ConcurrentHashMap<String, Object> data
. This Map
将存储实际的映射。
使用的原因ConcurrentHashMap
而不是普通的一个是它是线程安全的。这意味着不需要手动同步。该实现实际上划分了Map
分成扇区并仅锁定所需的扇区来执行操作 - 这使其更加高效。
现在,逻辑将是
-
putIfAbsent
a new ReentrantLock
into keys
。这将以线程安全的方式检查是否已经存在锁key
。如果没有,则添加新的,否则检索现有的。这意味着一把钥匙只能有一把锁
- 获得一把锁。这意味着您获得了对映射的独占访问权限。
- 做工作。如果是
TypeII
从中删除映射data
完成后。
- 解锁。
代码看起来像这样:
private final ConcurrentHashMap<String, Object> data = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Lock> keys = new ConcurrentHashMap<>();
private final ExecutorService executorService = null; //obviously make one of these
@RequiredArgsConstructor
private class TypeI implements Runnable {
private final String key;
private final Work work;
@Override
public void run() {
final Lock lock = keys.putIfAbsent(key, new ReentrantLock());
lock.lock();
try {
final Object value = data.get(key);
work.doWork(value);
} finally {
lock.unlock();
}
}
}
@RequiredArgsConstructor
private class TypeII implements Runnable {
private final String key;
private final Work work;
@Override
public void run() {
final Lock lock = keys.putIfAbsent(key, new ReentrantLock());
lock.lock();
try {
final Object value = data.get(key);
work.doWork(value);
data.remove(key);
} finally {
lock.unlock();
}
}
}
public static interface Work {
void doWork(Object value);
}
public void doTypeIWork(final String key, final Work work) {
executorService.submit(new TypeI(key, work));
}
public void doTypeIIWork(final String key, final Work work) {
executorService.submit(new TypeII(key, work));
}
我用过Lombok
注释以减少混乱。
这个想法是最小化或几乎消除公共资源锁定的数量,同时仍然允许Thread
如果需要,获得对特定映射的独占访问权。
清洁按键Map
您需要保证当前没有任何工作正在进行,并且没有Thread
在清理期间,s 会尝试获取任何锁。您可以通过尝试获取相关锁,然后从键映射中删除映射来完成此操作 - 这将确保当时没有其他线程正在使用该锁。
您可以运行一个计划任务,例如每 X 分钟从地图中清除 20 个键。如果您将其实现为 LRU 缓存,那么它应该相当干净。谷歌番石榴提供一个实现 http://docs.guava-libraries.googlecode.com/git/javadoc/com/google/common/cache/Cache.html你可以使用的。