gpt4 book ai didi

java - 如何在 HashMap 中实现入门级锁定?

转载 作者:行者123 更新时间:2023-11-29 07:33:47 30 4
gpt4 key购买 nike

因为,statckoverflow 不允许在原始问题中向您的问题添加更多内容(您只能添加评论,不能添加代码)我在这里问我的原始问题的顺序问题: Can we use Synchronized for each entry instead of ConcurrentHashMap?

问题很简单,我不知道为什么我之前可能很多人都遇到过这样一个简单的问题我要花这么多时间:/

问题是:我有一个 hashmap,我希望当一个线程正在处理 hashMap 的一个条目时,没有任何其他线程访问该对象,并且我不想锁定整个 hashMap。

我知道 java 提供了 ConcurrentHashMap,但是 ConcurrentHashMap 并没有解决问题,当你想做比简单的 put 和 get 更复杂的事情时。即使是像 merge 这样新添加的功能(在 Java 8 中)也不足以应对复杂的场景。

例如:

假设我想要一个将字符串映射到 ArrayList 的 HashMap 。然后例如假设我想这样做:对于键 k,如果有任何条目,则将 newString 添加到其 ArrayList,但如果没有 k 的条目,则为 k 创建条目,使其 ArrayList 具有 newString。

我想我可以这样做:

                ArrayList<String> tm =new ArrayList<String>();
tm.add(newString);
Object result = map.putIfAbsent(k, tm);
if (result != null)
{
map.get(k).add(newString);
}

但是它不起作用,为什么?假设 putIfAbset 返回 null 以外的东西,那么这意味着 map 已经有一个带有键 k 的条目,所以我将尝试将 newString 添加到已经存在的条目的 ArrayList 中,但是在添加之前,另一个线程可能会删除该条目,并且然后我会得到 NullPointerException!

因此,我发现很难正确编写此类代码。

但我在想,如果我能简单地锁定那个条目,生活就会很精彩!

在我之前的帖子中,我提出了一些非常简单的建议,实际上消除了对 concurrentHashMap 的需求,并提供了入门级锁定,但有人说这不是真的,因为 Long 不是不可变的……我没有很好地理解它.

现在,我实现并测试了它,它对我来说看起来不错,但我不知道为什么其他更有经验的开发人员告诉我它不是线程安全的:(

这是我测试过的确切代码:

主线程:

import java.util.HashMap;

public class mainThread {

public static HashMap<String, Long> map = new HashMap<String, Long>();

public static void main (String args[])
{
map.put("k1", new Long(32));


synchronized(map.get("k1"))
{
Thread t = new Thread(new threadA());
t.start();
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}
}
}

线程A:

public class ThreadA implements Runnable {

@Override
public void run() {
mainThread.map.put("k2", new Long(21));
System.out.println(mainThread.map.get("k2"));


synchronized (mainThread.map.get("k1")) {
System.out.println("Insdie synchronized of threadA");
}
}
}

效果很好!它打印 21,5 秒后,mainThread 释放 map.get("k1") 的锁,它打印 "Insdie synchronized of threadA"

那么,为什么使用这种简单的方法我们不能提供入门级锁定?!为什么并发应该那么复杂 Lol(开玩笑)

最佳答案

首先,据我所知,没有提供入门级锁定的标准 map 实现。

但我认为您可以避免这样做。例如

更新 ... 更正错误

ArrayList<String> tm = new ArrayList<String>();
ArrayList<String> old = map.putIfAbsent(k, tm);
if (old != null) {
tm = old;
}
synchronized (tm) {
// can now add / remove entries and this will appear as an atomic
// actions to other threads that are using `synchronized` to
// access or update the list
tm.add(string1);
tm.add(string2);
}

是的,另一个线程可能会在这个线程(可能)插入它和这个线程锁定它之间更新 hashmap 条目中的列表。不过,那没关系。 (更正的)putIfAbsent 和随后的测试确保每个人都将使用和锁定相同的列表。

(假设:所有线程在插入/更新条目时都使用此逻辑。)


如果列表变空,则原子删除列表很困难,但我认为通常没有必要这样做。


更新 2

有一个更好的方法:

ArrayList<String> tm = map.computeIfAbsent(k, ArrayList::new);
synchronized (tm) {
...
}

(感谢斯图尔特)


更新 3

We can do it with merger too.

也许,是的。像这样:

ArrayList<String> tm = new ArrayList<String>;
tm.add(...);
...
map.merge(key, tm, (oldV, newV) -> {oldV.addAll(newV); return oldV});

缺点是您正在双重处理 tm 的所有元素;即添加到 2 个单独的列表(其中一个你扔掉)。

但你也可以这样做:

map.merge(key, tm, (oldV, newV) -> {
oldV.removeAll(newV);
return oldV.size() == 0 ? null : oldV}
);

让我担心的是 javadoc没有明确声明值 oldV 将在发生这种情况时被锁定。它说:

"The entire method invocation is performed atomically. Some attempted update operations on this map by other threads may be blocked while computation is in progress ..."

...但它没有明确说明在发生这种情况时 存在互斥。 (例如,将此方法与 putIfAbsent/computeIfAbsent 和显式 synchronized block 混合使用很可能是危险的。锁定很可能是不同的对象。)

关于java - 如何在 HashMap 中实现入门级锁定?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38494042/

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