按照ConcurrentHashMap号公路上的JavaSE 8 docs

...即使所有操作都是线程安全的,检索操作也不需要锁定...

以上是如何发挥ConcurrentHashMapcompute功能的?请考虑以下简单对象

final class PlainObject{
  int a;
  int b;
}

请考虑以下映射

ConcurrentHashMap<String, PlainObject> myMap;

考虑以下可以访问myMap的函数

void act(){

  myMap.compute("abc", (k, v) -> {
    v.a = 21;
    // Other work 
    v.b = 18;

    return v;
  });
}

我是否可以在myMap上执行读取操作,以打印部分结果(即只有a被修改)?答案似乎是肯定的,因为检索数据不需要锁定,而且我们处理的是可变对象.这个结论正确吗?

推荐答案

你说得对.get()操作是"线程安全的",因为它保证要么返回在映射更新之前给定k存在的PlainObject,要么返回在该k下添加的新对象.它永远不会返回其他对象,也不会导致程序崩溃.

BUT

映射本身是线程安全的,这一事实绝不会保护您放置在映射in上的对象.如果您希望程序在一个线程希望与修改该对象的其他线程同时判断该对象时看到一致的值v.av.b,那么提供这种保护完全取决于您.


update:

您的示例没有按照预期的方式使用compute.您提供的函数永远不会返回与映射中已有的v不同的v.您编写的代码与此版本没有任何不同:

void act(){
  PlainObject v = myMap.get("abc");
  if (v != null) {
    v.a = 21;
    // Other work
    v.b = 18;
  }
}

compute的目的是让您编写一个函数来决定是否应该用不同的对象替换贴图中的对象:


void foo() {
  myMap.compute("abc", (k, v) -> {
    if ((v == null) || ...other reason not to update...) {
      return /*the original, UNMODIFIED*/ v;
    }
    else {
      PlainObject new_v = new PlainObject();
      new_v.a = 21;
      // Other work 
      new_v.b = 18;
      return new_v;
    }
  });

在这种情况下,如果某个其他线程调用myMap.get("abc"),则可以保证要么获取原始的未修改PlainObject,要么获取新对象.如果您的PlainObject个实例都是effectively immutable,*,那么当get()函数返回新对象时,它应该已经是safely published†了.

*如果在对象对其他线程可用之后,没有线程修改PlainObject,那么它将是"有效的不可变的".

†"安全发布"意味着在新对象的初始化和任何其他线程访问它的第一次机会之间存在一种"发生在此之前"的关系.

Java相关问答推荐

Java 8 RDX-如何设置单个选项卡标题文本的 colored颜色

将Nimbus设置为计算机上运行的所有Java应用程序的默认外观

在spring—data中自动发现native—sql查询期间遇到重复的SQL别名[id]

调用引发泛型异常的泛型方法时出现编译错误

Spark上下文在向Spark提交数据集时具有内容,但Spark在实际构建它时发现它为空

在运行MVN测试时,为什么构建失败,并显示了java.lang.ClassNotFoundException:java.net.http.HttpResponse?

蒙蒂霍尔比赛结果不正确

基于调车场算法的科学计算器

试着做一个2x2的魔方求解算法,我如何找到解路径(DFS)?

使用Room Database删除Jetpack合成中的所有项目后,UI未重新合成

在学习Spring时,通过构造函数参数0表达了不满意的依赖关系

在VS代码中,如何启用Java Main函数的&Q;Run|DEBUG&Q;代码?

有谁能帮我修一下这个吗?使输出变得更加整洁

IntelliJ IDEA依赖项工具窗口丢失

如何在SWT菜单项文本中保留@字符

如何使用jooq更新记录?

如何以事务方式向ibmmq发送消息

当我将鼠标悬停在javafxTextArea上时,如何更改鼠标光标?

如何在更改分辨率时将鼠标坐标计算为世界坐标

声纳覆盖范围为 0%,未生成 jacoco.xml