Simultaneous read write on value object of ConcurrentHashMap

2186 views java
6

I have a ConcurrentHashMap:

ConcurrentHashMap<ID,Object> map;

In my application, this map is high read and low write

Read works in following way:

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;
}

Update works in following way:

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());
        }
    }
}

Now my question is that,is ConcurrentHashMap is sufficient for above case to work in thread safe manner in multithreaded environment or I have to externally lock the Object by ReadWrite lock or use cloning of Object?

Suppose in a case when obj is read from map,ConcurrentHashMap will make sure it will return latest written object but what about when this object is removed/updated by writer thread just after read.Read object(which is already removed/updated from map) is used to prepare response object and its attribute are used to make certain decisions.

And what should be the better way to update map?

answered question

2 Answers

12

This is thread safe provided there is only one writer. Otherwise, there is a race condition between operation you perform. For this reason, there are operations which perform these actions atomically and simplify the code.

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;
    }
}

Most likely, you don't need a special update operation and you could make it also put

I would highly recommend not using Object as a custom class but instead, use a new name.

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

If the object is removed it has no impact on another thread holding that reference. If the object is updated by putting a new object in the map, this will be fine, however if you update the object added, this might not be thread safe.

posted this
3

For your code to be thread-safe with respect to the ConcurrentHashMap, you should be using the respective compute methods (which are performed atomically):

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;
}

And:

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());
    }
}

posted this

Have an answer?

JD

Please login first before posting an answer.