有许多线程可以处理同一个地图上的元素,每个线程都有一个代码,例如:
THREAD_1 works on an element with code "1234"
THREAD_2 works on an element with code "1234"
THREAD_3 works on an element with code "9876"
etc...
元素不是永久性的,即THREAD_1可能会删除元素“1234”,然后再次插入。我想要的是,虽然THREAD_1正在处理元素“1234”(同时删除它),但THREAD_2必须等待。
有办法吗?
一种可能的解决方案可能是在HashMap中插入假元素,然后使用该元素上的“synchronized”子句强制执行同步。你有什么想法? (显然,如果一个线程删除了带有相关代码的元素,假元素也会保留在地图中)...
答案 0 :(得分:2)
鉴于您的特定问题,Java标准对象都无法解决您的所有问题。这是一个我认为正确的解决方案,并且不会在锁定映射中保留任何不必要的键或值:
// we don't use a ConcurrentHashMap, because we have some other operations
// that need to be performed in atomically with map.put and map.remove.
// ConcurrentHashMap would of course also work, but it doesn't remove the
// need for external synchronization in in our case.
Map<String, CountingLock> locksMap = new HashMap<String, CountingLock>();
...
HttpResponse myFunction(String key) {
CountingLock lock;
synchronized(locksMap){
lock = locksMap.get(key);
if(lock == null){
lock = new CountingLock();
locksMap.put(key, lock);
}
lock.prepare(); // has to be done while holding the lock of locksMap.
// basically tells other threads that the current
// thread intends to acquire the lock soon. This way,
// the other threads know not to remove this lock
// from locksMap as long as another one has indicated
// that he is going to need it soon.
}
lock.lock(); // has to be done while NOT holding the lock of locksMap,
// or we risk deadlock situations.
try {
// ...
// work
// ...
} finally {
synchronized(locksMap) {
if(lock.unlock() == 0){
// no other thread is intending to use this lock any more.
// It is safe to remove it from the map. The next thread
// will just have to recreate a new lock for the same key.
locksMap.remove(key);
}
}
}
return SOMETHING;
}
private static class CountingLock {
// The number of threads that are trying to access the protected Key
private AtomicInteger interestedThreads = new AtomicInteger(0);
private Lock lock = new ReentrantLock();
public void prepare(){
interestedThreads.incrementAndGet();
}
public void lock(){
lock.lock();
}
public int unlock(){
lock.unlock();
return interestedThreads.decrementAndGet();
}
}
此代码应在所有情况下按预期工作。这是一个有趣的问题: - )
答案 1 :(得分:1)
我们使用的东西叫做LockMap。
LockMap本质上是:
Map<Object, ReadWriteLock>
我们有一个同步方法来获取特定对象的锁。
由于地图依赖于等价而不是同一性,因此equal()
的两个对象会给你相同的锁定。
所以:
lock1 = map.get(new Thing(1));
lock2 = map.get(new Thing(1));
lock1 == lock2 = true
哪个方便。
获得锁定后,您可以锁定它,以便控制对对象的访问。
我们要做的另一件事是使用LRU映射(使用LinkedHashMap - 请参阅this),以便旧对象密钥在未使用时可以脱离结束。
答案 2 :(得分:0)
您应该使用ConcurrentHashMap
一个哈希表,支持检索的完全并发和可更新的预期并发性。
检索操作(包括get)通常不会阻止,因此可能与更新操作重叠(包括put和remove)。
更新操作之间允许的并发性由可选的concurrencyLevel构造函数参数(缺省值16)引导,该参数用作内部大小调整的提示。该表在内部进行分区,以尝试允许指定数量的并发更新而不会发生争用。因为散列表中的放置基本上是随机的,所以实际的并发性会有所不同
答案 3 :(得分:0)
关于名称/键的锁定有一个类似的问题,并讨论了这个主题:Simple Java name based locks?