有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

ConcurrentHashMap值对象的java同步读写

我有一个ConcurrentHashMap:

ConcurrentHashMap<ID,Object> map;

在我的应用程序中,这个映射是高读低写的

以以下方式阅读作品:

public Response getObject() {
    Response response = createResponse();
    Object obj = map.get(ID);
    if (obj != null) {
        if (obj.getAttribute1() == some_value) {
            response.setAttr1(obj.getAttr1());
            response.setAttr2(obj.getAttr2());
        }
    }
    return response;
}

更新的工作方式如下:

public void updateObject(Object obj, int action) {
    if (action == ADD) {
        map.put(obj.getID(), obj);
    } else if (action == UPDATE) {
        object oldObj = map.get(obj.getID());
        if (oldObj != null) {
            map.put(obj.getID(), obj);
        }
    } else if (action == REMOVE) {
        object oldObj = map.get(obj.getID());
        if (oldObj != null) {
            map.remove(obj.getID());
        }
    }
}

现在我的问题是,ConcurrentHashMap是否足以让上述情况在多线程环境中以线程安全的方式工作,或者我必须通过读写锁或使用对象克隆从外部锁定对象

假设在从map读取obj的情况下,ConcurrentHashMap将确保返回最新写入的对象,但当写入线程在读取后删除/更新该对象时又会怎样呢。Read对象(已从映射中删除/更新)用于准备响应对象,其属性用于做出某些决策

更新地图最好的方法是什么


共 (2) 个答案

  1. # 1 楼答案

    为了使代码对于ConcurrentHashMap是线程安全的,应该使用相应的compute方法(以原子方式执行):

    public Response getObject() {
        Response response = createResponse();
    
        map.computeIfPresent(ID, (k, v) -> {
            if (v == some_value) {
                response.setAttr1(v.getAttr1());
                response.setAttr2(v.getAttr2());
            }
    
            return v;
        }
    
        return response;
    }
    

    以及:

    public void updateObject(Object obj, int action) {
        if (action == ADD) {
            map.put(obj.getID(), obj);
        } else if (action == UPDATE) {
            map.computeIfPresent(obj.getID(), (k, v) -> obj);
        } else if (action == REMOVE) {
            map.remove(obj.getID());
        }
    }
    
  2. # 2 楼答案

    这是线程安全的,前提是只有一个写入程序。否则,您执行的操作之间存在竞争条件。因此,有些操作可以原子化地执行这些操作,并简化代码

    public void updateObject(Object obj, int action) {
        switch (action) {
            case ADD:
                map.put(obj.getID(), obj);
                break;
    
            case UPDATE:
                map.computeIfPresent(obj.getID(), (k, v) -> obj);
                break;
    
            case REMOVE:
                map.remove(obj.getID());
                break;
        }
    }
    

    最有可能的是,您不需要特殊的更新操作,也可以使其put

    我强烈建议不要使用Object作为自定义类,而是使用新名称

    what about when this object is removed/updated by writer thread just after read

    如果对象被移除,它对另一个持有该引用的线程没有影响。如果对象是通过在地图中放置新对象来更新的,这将很好,但是如果更新添加的对象,这可能不是线程安全的