Search code examples
javajava.util.concurrentconcurrenthashmap

applying concurrency when a ConcurrentHashMap is map value


Multiple threads will try to update values of same mainCode, does below cause trouble? Also another job will consistently read values of securities and apply operations if values match.

public class MyObj {

        private static ConcurrentHashMap<String, ConcurrentHashMap<String, Model>> securities = new ConcurrentHashMap<>();
    
        private static void updateModel(String mainCode, String code, BigDecimal valueA, BigDecimal valueB) {
            securities.compute(mainCode, (k, v) -> {
                modifyValue(v, valueA, valueB, code);
                return v;
            });
        }
    
        private static void modifyValue(ConcurrentHashMap<String, Model> v, BigDecimal valueA, BigDecimal ValueB, String code) {
            v.compute(code, (k, val) -> {
                val.setCode(code);
                val.setValueA(valueA);
                val.setValueB(ValueB);
                return val;
            });
    
        }
    
    }

Solution

  • Interpreting your data structure based on the code you're using to access it, you have an outer map from String codes to inner singleton maps, and each inner map maps exactly one code to a Model. The Models have multiple distinct properties that are to be updated, as a group, from time to time. Each key of the outer map is the same as the single key of the corresponding inner map. Each key of each inner map is the same as the code property of the corresponding Model.

    I guess, then, the idea for the inner maps, which otherwise seem redundant, is to use their ConcurrentHashMap.compute() methods to perform modifications of multiple properties of the Model objects as atomic groups, without blocking concurrent modification of different Model objects.

    Multiple threads will try to update values of same mainCode, does below cause trouble?

    Each entire invocation of ConcurrentHashMap.compute() is performed atomically, so multiple threads invoking securities.compute() via your updateModel() method, whether for the same or for different keys, will not interfere with each other with respect to the securities map itself.

    Similarly, multiple threads concurrently invoking modifyValue() with the same first argument will not interfere with each other with respect to that map. The Model-property modifications will be performed as an atomic group with respect to other operations on the map.

    But that's not sufficient. If those properties of your Model objects are accessed by any other path (and surely they are at least read at some point), then that produces data races. At minimum, those reads can observe inconsistent combinations of property values.

    It might be possible to build sufficient additional infrastructure around this nested-map idea to serve all your needs without data races, but this cure is probably worse than the disease. I see two potential approaches that are superior:

    1. Make the Model objects immutable, and remove the inner maps (the outer map will map codes directly to Models). Instead of updating existing Model objects, replace them with new ones having the desired properties.

    OR

    1. Apply explicit locks or conventional synchronization appropriately to protect all accesses to the Model objects, and remove the inner maps.

    If (1) is viable for you with respect to the other requirements of your application then that's what I would recommend. If not, then probably there is no alternative better than (2).