Java 如何在HashMap中实现入门级锁定?
由于statckoverflow不允许在原始问题中向您的问题添加更多内容(您只能添加注释,不能添加代码),因此我在这里向我的原始问题提出一个顺序问题: 这个问题很简单,我不知道为什么在我之前很多人都遇到过这样一个简单的问题,我应该花这么多时间:/ 问题是:我有一个hashmap,我希望当一个线程处理hashmap的一个条目时,没有任何其他线程访问该对象,并且我不想锁定整个hashmap 我知道java提供了ConcurrentHashMap,但是当你想做比简单的put-and-get更复杂的事情时,ConcurrentHashMap并不能解决这个问题。即使是新添加的函数(在Java8中),比如merge,对于复杂的场景也是不够的 例如:Java 如何在HashMap中实现入门级锁定?,java,concurrency,hashmap,Java,Concurrency,Hashmap,由于statckoverflow不允许在原始问题中向您的问题添加更多内容(您只能添加注释,不能添加代码),因此我在这里向我的原始问题提出一个顺序问题: 这个问题很简单,我不知道为什么在我之前很多人都遇到过这样一个简单的问题,我应该花这么多时间:/ 问题是:我有一个hashmap,我希望当一个线程处理hashmap的一个条目时,没有任何其他线程访问该对象,并且我不想锁定整个hashmap 我知道java提供了ConcurrentHashMap,但是当你想做比简单的put-and-get更复杂的事
Long intialValue = new Long(3);
Long addedValue = new Long(10);
Long removingValue = new Long (5);
ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, Long>> map = new ConcurrentHashMap<>();
//Initialization....
ConcurrentHashMap<Integer, Long> i = new ConcurrentHashMap<Integer, Long>();
i.put(1, intialValue);
map.put(23, i);
//......
//addition
ConcurrentHashMap<Integer, Long> c = new ConcurrentHashMap<Integer, Long>();
c.put(1, addedValue);
map.merge(23, c, (oldHashMap, newHashMap) -> {
oldHashMap.merge (1, c.get(1), (oldV, newV) -> {
if (oldV < newV) return newV; else return oldV;
});
return oldHashMap;
});
//removal
// we want to remove entry 1 from the inner HashMap if its value is less than 2, and if the entry is empty remove the entry from the outer HashMap
ConcurrentHashMap<Integer, Long> r = new ConcurrentHashMap<Integer, Long>();
r.put(1, removingValue);
map.merge (23, r, (oldHashMap, newHashMap) -> {
oldHashMap.merge(1, newHashMap.get(1), (oldV, newV) -> {if (oldV < newV) return newV; else return oldV;});
return oldHashMap;
});
map.remove(23, r);
if (map.containsKey(23))
{
System.out.println("Map contains key 23");
if (map.get(23).containsKey(1))
{
System.out.println("The value for <23,1> is " + map.get(23).get(1));
}
}
假设我需要一个将字符串映射到ArrayList的哈希映射。例如,假设我想这样做:
对于键k,如果有任何条目,则将新闻字符串添加到其ArrayList,但如果没有k的条目,则为k创建条目,使其ArrayList具有新闻字符串。
我想我可以这样做:
ArrayList<String> tm =new ArrayList<String>();
tm.add(newString);
Object result = map.putIfAbsent(k, tm);
if (result != null)
{
map.get(k).add(newString);
}
很好用!它打印21,5秒后,该主线程释放map.get(“k1”)的锁,它打印“线程A的Insdie synchronized”
那么,为什么使用这种简单的方法我们不能提供入门级锁定呢?!为什么并发应该如此复杂Lol(开玩笑)首先,我知道没有提供入门级锁定的标准映射实现 但我认为你可以避免这种需要。比如说 更新。。。纠正错误
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);
}
(谢谢斯图尔特)
更新3 我们也可以通过合并来实现 也许,是的。大概是这样的:
ArrayList<String> tm = new ArrayList<String>;
tm.add(...);
...
map.merge(key, tm, (oldV, newV) -> {oldV.addAll(newV); return oldV});
我关心的是,在发生这种情况时,没有明确声明值oldV
将被锁定。它说:
“整个方法调用是以原子方式执行的。在计算过程中,其他线程在此映射上尝试的某些更新操作可能会被阻止…”
。。。但它并没有明确说明在发生这种情况时,该值存在互斥性。(例如,将此方法与
putIfAbsent
/computeIfAbsent
和显式synchronized
块混合使用很可能是危险的。锁定很可能在不同的对象上。),第一个大问题是,您甚至没有尝试对put
调用执行任何锁定。对于常规HashMap,这些不是自动线程安全的。您似乎觉得单独的HashMap条目是完全独立的,但HashMap不是这样工作的
即使您解决了put
问题(可能需要ConcurrentHashMap或整个map锁),您实际锁定的部分也不会安全锁定
假设线程1put
s输入“k1”:1
,线程2尝试get(“k1”)
。线程2将看到什么
好的,线程2甚至在get
调用完成之前都不会尝试获取任何锁。get
调用完全不受保护!如果put
和get
之间没有任何before关系,get
调用可能看不到条目,或者它可能看到条目,或者它可能看到处于不一致中间状态的映射并可怕地崩溃
根据
get
调用的结果进行同步太晚了。我想我终于找到了使用merge函数的解决方案。我举一个例子,我会编辑这篇文章,让其他人更容易阅读,但我只是现在发布,以获得您的反馈
以下是ConcurrentHashMap的示例,其值为ConcurrentHashMaps(为了示例起见,23和1只是两个随机值):
Long intialValue=新的Long(3);
长加值=新长(10);
长距离移动值=新长距离(5);
ConcurrentHashMap=新的ConcurrentHashMap();
//初始化。。。。
ConcurrentHashMap i=新的ConcurrentHashMap();
i、 put(1,初始值);
地图.put(23,i);
//......
//加成
ConcurrentHashMap c=新的ConcurrentHashMap();
c、 put(1,加法值);
merge(23,c,(oldHashMap,newHashMap)->{
oldHashMap.merge(1,c.get(1),(oldV,newV)->{
if(oldV{
合并(1,newHashMap.get(1),(oldV,newV)->{if(oldV
代码就是这样做的:
initialValue
李>
addedValue
,它将用addedValue
覆盖它,否则它将不处理它李>
ArrayList<String> tm = map.computeIfAbsent(k, ArrayList::new);
synchronized (tm) {
...
}
ArrayList<String> tm = new ArrayList<String>;
tm.add(...);
...
map.merge(key, tm, (oldV, newV) -> {oldV.addAll(newV); return oldV});
map.merge(key, tm, (oldV, newV) -> {
oldV.removeAll(newV);
return oldV.size() == 0 ? null : oldV}
);
Long intialValue = new Long(3);
Long addedValue = new Long(10);
Long removingValue = new Long (5);
ConcurrentHashMap<Integer, ConcurrentHashMap<Integer, Long>> map = new ConcurrentHashMap<>();
//Initialization....
ConcurrentHashMap<Integer, Long> i = new ConcurrentHashMap<Integer, Long>();
i.put(1, intialValue);
map.put(23, i);
//......
//addition
ConcurrentHashMap<Integer, Long> c = new ConcurrentHashMap<Integer, Long>();
c.put(1, addedValue);
map.merge(23, c, (oldHashMap, newHashMap) -> {
oldHashMap.merge (1, c.get(1), (oldV, newV) -> {
if (oldV < newV) return newV; else return oldV;
});
return oldHashMap;
});
//removal
// we want to remove entry 1 from the inner HashMap if its value is less than 2, and if the entry is empty remove the entry from the outer HashMap
ConcurrentHashMap<Integer, Long> r = new ConcurrentHashMap<Integer, Long>();
r.put(1, removingValue);
map.merge (23, r, (oldHashMap, newHashMap) -> {
oldHashMap.merge(1, newHashMap.get(1), (oldV, newV) -> {if (oldV < newV) return newV; else return oldV;});
return oldHashMap;
});
map.remove(23, r);
if (map.containsKey(23))
{
System.out.println("Map contains key 23");
if (map.get(23).containsKey(1))
{
System.out.println("The value for <23,1> is " + map.get(23).get(1));
}
}