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

Can we achieve read on mutable data after write atomically in ConcurrentHashMap in Java?

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

我的实现是这样的:

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

如果我这样做

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

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

现在,我在想如果我喜欢以下内容:它会是一个原子 operation/in 换句话说,它会阻塞 key1 段吗?

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

Javacompute 函数的文档

Attempts to compute a mapping for the specified key and its current mapped value (or null if there is no current mapping). 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, so the computation should be short and simple, and must not attempt to update any other mappings of this 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;
        }
    }

你混淆了两个不同的术语。
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}},所以我们的操作是线程安全的。如果这就是你想要的,那很好。

ConcurrentHashMap.compute 的 Javadoc 状态

The entire method invocation is performed atomically.

注意,相比之下 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]