Search code examples
javahashmapthread-safetymapsunmodifiable

Is updating reference of unmodifiableMap thread safe


I have a map (let's call it original map), which is initially null. During service deployment and every hour after that, I need to refresh this map or basically reassing it.

Here is how I do it. In the refresh, I create a new map, and return an unmodifiableMap view of that new map, to my original map, now while this reassignment happens, that is the reference for original map gets changed, will it impact any other thread currently accessing the original map? Thing to note is during the service deployment, the original map is assigned a value in the similar fashion, basically the same refresh strategy is used.

        private static Map<String, PricingPriceList> plInfoByName;
        TransactionData.plInfoByName = plInfo.get(0);

Here plInfoByName is my original map, and plInfo contains a list of unmodifable maps. Here is how plInfo list is populated

    Map<String, PricingPriceList> plInfoByName = new HashMap<String, PricingPriceList>();
    Map<String, PricingPriceList> plInfoById = new HashMap<String, PricingPriceList>();

    try {
        stmt = dbConn.createStatement();
        stmt.setFetchSize(10000);

        rs = stmt.executeQuery(query);
        PricingPriceList plDetails = null;
        while (rs.next()) {
            plDetails = new PricingPriceList();

            //populate plDetails attributes

            plInfoByName.put(rs.getString(0), plDetails);
            plInfoById.put(rs.getString(1), plDetails);
        }

    } catch (Exception e) {
        LOGGER.ERROR("Error executing refreshPlInfo. Affected in-memory objects: plInfoByName, plInfoById.", e);
    } finally {
        try {
            if (stmt != null && !stmt.isClosed()) {
                stmt.close();
            }
            if (rs != null && !rs.isClosed()) {
                rs.close();
            }
        } catch (SQLException e) {
            LOGGER.ERROR("refreshPlInfo failed to close SQL statement or resultset.", e);
        }

    }

    // Return unmodifiable version
    List<Map<String, PricingPriceList>> plInfo = new ArrayList<Map<String, PricingPriceList>>();
    plInfo.add(Collections.unmodifiableMap(plInfoByName));
    plInfo.add(Collections.unmodifiableMap(plInfoById));
    return plInfo;

So when I do this, will it impact any thread reading TransactionData.plInfoByName? Or is it thread safe cause it's an unModifiableMap that is stored in it.

    TransactionData.plInfoByName = plInfo.get(0);

Solution

  • An unmodifiableMap isn't threadsafe by itself, it just prevents users from changing it. Another thread that has access to the underlying map could still change it while the current thread is reading.

    However, if you just change the reference to the map, it should not affect any thread that is currently accessing the "old" map. Assuming (I'd have to check that) that getting the reference to an object is a more or less atomic operation (see here: What operations in Java are considered atomic?), any thread that got the reference to the "old" map should keep it until it retrieves the reference again.

    Example:

    Assume the following operations:

    • variable "map" contains the reference to map A
    • T1 retrieves map A through "map" and stores that reference in some local variable, let's call call it "t1Map"
    • T2 changes "map" to refernence map B now
    • T1 accesses the map through "t1Map" which still references A
    • T1 retrieves map A through "map" again and will now get the reference to B