在Java的ConcurrentHashMap中原子写入后,我们能否实现对可变数据的读取?

时间:2018-07-28 07:30:15

标签: java concurrenthashmap

我正在尝试找到这些问题的答案,但无法在Google或Java文档中理解(或确认)它。

我的实现如下:

Map<String, POJO> map = new ConcurrentHashMap<String, POJO>();

如果我愿意

value1 = map.get(key1);
value1.setProp(prop);

任何其他线程都可以覆盖。

现在,我正在考虑是否要执行以下操作:这将是原子操作/换句话说,它将阻塞key1段?

map.compute(key1, (key1, value1) -> { value1.setProp(prop) });

用于compute函数的Javadoc

  

尝试计算指定键及其当前键的映射   映射值(如果没有当前映射,则为null)。整个   方法调用是原子执行的。一些尝试更新   其他线程对此地图进行的操作可能会在   计算正在进行中,因此计算应简短且   简单,并且不得尝试更新此Map的任何其他映射。

参考文献:

1。https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html  2。https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ConcurrentHashMap.html#compute-K-java.util.function.BiFunction-

编辑:

在我的最终实现中,我做了这样的事情,因为所有线程都共享NewMap,最后,我列出了POJO的新列表
抽象数据类型

    public class NewMap {

        private Map<String, POJO> map;
        private boolean isUpdatable;
        void NewMap(){
              isUpdatable = true;
              map = new ConcurrentHashMap();
        }

        void putPOJOProp1(String key, Type value) throws ReadOnlyException{

                  map.compute(key, (k,v) -> {
                           if(!isUpdatable) throw new ReadOnlyException();
                           if(k == null){ 
                              POJO p = new POJO();
                              p.setProp1(value);
                              v = p;
                           } else { 
                              v = v.setProp1(v)
                           }
             });
        }

        void putPOJOProp2....
        void putPOJOProp3....

        List<POJO> getAll() {
             isUpdatable = false;
             List<POJO> pojos;
             for(key: map.getKeys()) {
                  Pojo p = map.get(key);
                  p.setKey(key);
                  pojos.add(p);
             }
             return pojos;
        }
    }

2 个答案:

答案 0 :(得分:8)

您会混淆两个不同的术语。
ConcurrentHashMap防止内部结构损坏,但不能防止竞争条件。如果您只想避免数据损坏,那么使用ConcurrentHashMap是有效的。
但是从您的问题看来,您似乎正在努力避免比赛条件。 ConcurrentHashMap不会以任何方式保护您免受他们侵害。

为了更好地理解它,让我们看下面的示例:

    Map<String, POJO> map = new ConcurrentHashMap<>();
    ExecutorService pool = Executors.newWorkStealingPool(10);

    for (int t = 0; t < 10; t++) {
        pool.execute(() -> {
            for (int i = 0; i < 100_000; i++) {
                map.compute("A", (k, v) -> {
                    if (v == null) {
                        return new POJO();
                    }
                    v.a = ++v.a;
                    v.b = ++v.b;

                    return v;
                });
            }
        });
    }

    pool.awaitTermination(5, TimeUnit.SECONDS);

    System.out.println(map);

    // With POJO like 
    class POJO {
       // toString() here
       Integer a = 1;
       Integer b = 1;
    }

在这里我们得到{A=POJO{a=1000000, b=1000000}},因此我们的操作是线程安全的。如果您只想得到这些,那就没事了。

答案 1 :(得分:8)

ConcurrentHashMap.compute状态的Javadoc

  

整个方法调用是原子执行的。

请注意,相比之下ConcurrentSkipListMap.compute不是原子的。

Alexey Soshin回答的更紧凑形式是

Map<String, long[]> map = new ConcurrentSkipListMap<>();
map.put("A", new long[2]);
IntStream.range(0, 1_000_000)
        .parallel()
        .forEach(i -> map.compute("A", (k, v) -> {
            v[0]++;
            v[1]++;
            return v;
        }));
System.out.println(Arrays.toString(map.get("A")));

打印类似

[643553, 597254]

c.f。 HashMap产生类似

[244786, 245993]

但是,使用ConcurrentHashMap可以达到预期的效果

[1000000, 1000000]