更新 unmodifiableMap 线程安全的引用

Is updating reference of unmodifiableMap thread safe

我有一张地图(我们称之为原始地图),它最初是空的。在服务部署期间以及之后的每个小时,我都需要刷新这张地图或者基本上重新评估它。

这是我的做法。在刷新中,我创建了一个新地图,并且 return 那个新地图的 unmodifiableMap 视图,到我的原始地图,现在当这个重新分配发生时,原始地图的参考被改变了,它会影响任何其他地图吗当前正在访问原始地图的线程?需要注意的是,在服务部署的时候,原图也是以类似的方式赋值,基本使用相同的刷新策略。

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

这里的plInfoByName是我的原始地图,plInfo包含一个不可修改的地图列表。以下是 plInfo 列表的填充方式

    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;

所以当我这样做时,它会影响任何线程读取 TransactionData.plInfoByName 吗?或者它是线程安全的,因为它是一个存储在其中的 unModifiableMap。

    TransactionData.plInfoByName = plInfo.get(0);

unmodifiableMap 本身不是线程安全的,它只是防止用户更改它。另一个有权访问底层地图的线程仍然可以在当前线程读取时更改它。

但是,如果您只是更改对地图的引用,它应该不会影响当前正在访问 "old" 地图的任何线程。假设(我必须检查)获取对对象的引用或多或少是一个原子操作(参见此处:What operations in Java are considered atomic?),任何获得对 "old" 映射的引用的线程都应该保留它直到它再次检索引用。

示例:

假设以下操作:

  • 变量 "map" 包含对地图 A 的引用
  • T1 通过 "map" 检索映射 A 并将该引用存储在某个局部变量中,让我们调用它 "t1Map"
  • T2现在"map"改为参考图B
  • T1 通过 "t1Map" 访问映射,它仍然引用 A
  • T1 再次通过 "map" 检索映射 A,现在将获得对 B
  • 的引用