我有一个场景,我有一个method(int id)
,里面有一个关键部分。当多个线程使用不同的id调用此方法时,这些多个线程可以被授予访问临界区的权限,但是当具有id
(方法参数)“x”的新线程尝试进入已经具有具有相同id的线程,其他线程不应该进入。
请知道是否需要更多信息。
答案 0 :(得分:3)
您可以使用包含关键部分的类的私有ConcurrentHashMap<Integer, Object>
,并为每个ID保存一个监视器对象:
private final ConcurrentHashMap<Integer, Object> locks = new ConcurrentHashMap<>();
然后
synchronized (locks.computeIfAbsent(id, id -> new Object())) {
// Critical section.
}
当遇到新ID时,它会以原子方式将对象插入到地图中,并将其返回;然后在任意对象上同步该块。
遇到相同的ID时,返回先前创建的对象;这可以防止具有相同ID的两个线程同时在块中。
答案 1 :(得分:1)
我猜测一个简单的解决方案是将ID存储在列表中,并检查线程的ID是否已经在列表中。线程完成关键代码后,从列表中删除其ID。
通常,为了防止访问关键部分,您可以使用同步块,但由于您只需要阻止访问“重复”线程,我认为不能这样做。
答案 2 :(得分:0)
您可以将监视器/锁存储到ConcurrentHashMap中,并通过id检索相应的锁。如果您希望能够从地图中删除不必要ID的Map,则可以使用WeakReferences。处理起来有点棘手但是可行。
弱引用确保如果锁定对象仍被至少一个其他线程引用,则所有线程都使用该实例。如果没有使用它WeakReference.get()
将返回null。
类似的东西:
private static final ConcurrentMap<Integer, WeakReference<Object>> MONITORS = new ConcurrentHashMap<>();
public void method(int id) {
Object lock = getLock(id);
synchronized (lock) {
}
}
private Object getLock(int id) {
Object newLock = new Object();
WeakReference<Object> newReference = new WeakReference<>(newLock);
WeakReference<Object> oldReference = MONITORS.computeIfAbsent(id, _ -> new WeakReference<>(newLock));
Object oldLock = oldReference.get();
while (oldLock == null) {
if (MONITORS.replace(id, oldReference, newReference)) {
return newLock;
} else {
oldReference = MONITORS.get(id);
}
}
return oldLock;
}
private void maintain() {
for (Iterator<WeakReference<Object>> iterator = MONITORS.values().iterator(); iterator.hasNext();) {
WeakReference<Object> reference = iterator.next();
if (reference.get() == null) {
iterator.remove();
}
}
}
答案 3 :(得分:0)
阅读这个问题时。我想到了信号量的实现。本质上,它允许您为java代码中的资源或逻辑集创建固定数量的访问许可。在您的对象内部使用关键资源来访问关键代码,您可以实现一些逻辑,让访问线程获取许可或被拒绝。关于这一点的好处是利用关键资源的线程可以在完成后将许可证返回池。
这是关于它的信息的Java doc链接: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Semaphore.html