Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/384.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在HashMap中实现入门级锁定?_Java_Concurrency_Hashmap - Fatal编程技术网

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

Java 如何在HashMap中实现入门级锁定?,java,concurrency,hashmap,Java,Concurrency,Hashmap,由于statckoverflow不允许在原始问题中向您的问题添加更多内容(您只能添加注释,不能添加代码),因此我在这里向我的原始问题提出一个顺序问题: 这个问题很简单,我不知道为什么在我之前很多人都遇到过这样一个简单的问题,我应该花这么多时间:/ 问题是:我有一个hashmap,我希望当一个线程处理hashmap的一个条目时,没有任何其他线程访问该对象,并且我不想锁定整个hashmap 我知道java提供了ConcurrentHashMap,但是当你想做比简单的put-and-get更复杂的事

由于statckoverflow不允许在原始问题中向您的问题添加更多内容(您只能添加注释,不能添加代码),因此我在这里向我的原始问题提出一个顺序问题:

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

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

我知道java提供了ConcurrentHashMap,但是当你想做比简单的put-and-get更复杂的事情时,ConcurrentHashMap并不能解决这个问题。即使是新添加的函数(在Java8中),比如merge,对于复杂的场景也是不够的

例如:

    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锁),您实际锁定的部分也不会安全锁定

假设线程1
put
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
代码就是这样做的:

  • 初始化:首先为键23创建映射并将另一个映射放入其中,该映射具有键1的值
    initialValue
  • 加法:然后检查,1)如果键23没有值,它将为键1放置一个具有值addedValue的映射,否则2)如果键23已经有值,它将检查其值如果该值小于
    addedValue
    ,它将用
    addedValue
    覆盖它,否则它将不处理它
  • 删除:最后,检查是否存在ke
    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));
            }
        }