gpt4 book ai didi

java - 在Java中,多个线程想要操作一个对象,如何让一个对象操作该对象,而其他线程等待工作完成?

转载 作者:行者123 更新时间:2023-12-01 16:56:50 24 4
gpt4 key购买 nike

我的问题有点复杂:我有一个并发 map ,线程想要访问并更新 map ,如果两个线程想要获取 map 的相同条目,一个应该首先获取 map ,更新它,另一个应该等到条目更新成功,然后获取条目。我最初的想法是:我可以使用另一个并发映射,与目标映射相同的键并使用闩锁作为其值。我的代码是这样的:

private final ConcurrentMap<Long, List<RowKeyMap>> targetmap;    
private final ConcurrentMap<Long, CountDownLatch> helpermap;
long keyMillis; //key


CountDownLatch restoreLatch = helpermap.get(keyMillis);

if (restoreLatch != null) {
try {
restoreLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted trying to get " + keyMillis);
}
}

List<RowKeyMap> restoredata = targetmap.get(keyMillis);

if (restoredata == null) {
//find the entry should be restored, put a latch into the helpermap and restore it
restoreLatch = new CountDownLatch(1);
CountDownLatch existingLatch = helpermap.putIfAbsent(keyMillis, restoreLatch);

if (existingLatch == null) {
microshards = new ArrayList<>(count);

for (int i = 0; i < count; ++i) {
microshards.add(new RowKeyMap(some parameters));
}
List<RowKeyMap> existing = targetmap.putIfAbsent(keyMillis, microshards);

if (existing == null) {
{do actual restore job here}
} else {
microshards = existing;
}
restoreLatch.countDown();
restoresByDate.remove(keyMillis);
} else {
// Lost the race, wait for the restore task is complete and get the new restoredata
try {
existingLatch.await();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Interrupted trying to get " + keyMillis);
}
{get the new restoredata}
}
}

但是当前版本有一个错误:

  • 线程 A 执行第一行,restoreLatch 为 null
  • 线程 B 唤醒并执行第一行,也获取 null恢复锁
  • 线程 B 继续执行以下几行,看到executionLatch 为 null
  • 线程 B 继续,将已创建但尚未恢复的列表放入列表中进入恢复数据
  • 线程A唤醒并执行通过,得到已创建但尚未从恢复数据恢复到列表

有人有解决这个问题的想法吗?谢谢!

最佳答案

所以你想要的是每个映射条目都有一个锁。我不确定CountDownLatch在这里是理想的,因为它不能重复使用,并且每次创建一个新的都会使您的问题变得复杂。

但你的基本问题是你没有阻止锁本身的竞争条件。

为了做到这一点,您必须首先确保该条目存在一个锁对象,并且如果两个线程访问同一个条目,它们将获得相同的锁

您可以通过首先创建一个锁对象,然后使用 putIfAbsent 来做到这一点将其放入锁定映射中:

Object entryLock = new Object();
Object returnedLock = helpermap.putIfAbsent( keyMillis, entryLock );
entryLock = returnedLock == null ? entryLock : returnedLock;

这样做的目的是确保尝试访问同一条目 ( keyMillis ) 的任何两个线程都将获得相同的锁实例。如果线程 A 是第一个运行 putIfAbsent行,那么它在第一行中创建的新对象将被放置在辅助映射中,并且它将得到 null返回,这意味着它还将使用刚刚放置在 map 中的对象 - entryLock 。然后线程 B 出现并创建自己的 entryLock 。但是当它尝试putIfAbsent时行,已经有一个对象映射到 keyMillis , returnedLock ,这就是它将使用的对象(在这种情况下,它创建的原始新锁将被丢弃到垃圾回收中)。

因此,无论您按哪个顺序,都会到达 putIfAbsent行,他们将使用相同的锁实例。现在下一步是:

  • 锁上锁。
  • 运行对 targetMap 中数据的处理,如果不存在则创建它,如果存在则更新它,等等。一直以来,其他线程都在处理这个特定的 keyMillis正在等待,但有其他 keyMillis 的线程不要。
  • 解锁。等待此的其他线程之一 keyMillis输入现在将锁定锁。

要做到这一点非常简单:

synchronized(entryLock) {
// All operations on the particular entry
}

如果您需要更高级的锁设施,请使用ReentrantLockCyclicBarrier 。一个CountDownLatch需要用新的替换才能使用,这会破坏上面的安排,后者很大程度上依赖于所有线程的锁对象都是相同的。

关于java - 在Java中,多个线程想要操作一个对象,如何让一个对象操作该对象,而其他线程等待工作完成?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31570231/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com