You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程更新同一mainCode的ConcurrentHashMap值,并发读取是否有问题?

多线程更新与读取ConcurrentHashMap的代码风险分析

多个线程尝试更新同一个mainCode对应的值时,以下代码是否会引发问题?此外,另有一个作业会持续读取securities的值,并在值匹配时执行相应操作。

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

    }

}

存在的问题

1. 空指针异常(NullPointerException)

当首次更新某个mainCode时,securities中不存在该key,compute方法传入的参数v为null。此时调用modifyValue方法,直接对null执行v.compute(code, ...)会触发空指针异常。

2. Model实例的线程安全隐患

假设Model类未做线程安全处理(如下示例):

class Model {
    private String code;
    private BigDecimal valueA;
    private BigDecimal valueB;

    // 普通setter、getter方法
    public void setCode(String code) { this.code = code; }
    public void setValueA(BigDecimal valueA) { this.valueA = valueA; }
    public void setValueB(BigDecimal valueB) { this.valueB = valueB; }
}
  • 可见性问题:如果Model的字段未用volatile修饰,多线程修改时,持续读取的作业可能无法及时看到最新的字段值。
  • 状态不一致:setCode、setValueA、setValueB是三个独立操作,并非原子性。读作业可能看到部分更新的状态(比如valueA已更新,但valueB还是旧值)。

3. ConcurrentHashMap.compute方法的误用

securities.compute(mainCode, ...)的目的是修改内层ConcurrentHashMap的内容,而非替换内层map本身。当前写法不仅容易引发NPE,还不如使用computeIfAbsent更高效安全——computeIfAbsent可以确保内层map存在时直接返回,不存在则创建新实例。

修正方案

1. 解决空指针问题

将updateModel方法修改为:

private static void updateModel(String mainCode, String code, BigDecimal valueA, BigDecimal valueB) {
    // 确保内层map存在,不存在则创建
    ConcurrentHashMap<String, Model> innerMap = securities.computeIfAbsent(mainCode, k -> new ConcurrentHashMap<>());
    modifyValue(innerMap, valueA, valueB, code);
}

2. 确保Model的线程安全

可以选择以下方案之一:

  • 字段加volatile修饰:让字段的修改对其他线程可见:
    class Model {
        private volatile String code;
        private volatile BigDecimal valueA;
        private volatile BigDecimal valueB;
    
        // setter和getter不变
    }
    
  • 使用同步方法:将setter、getter方法改为同步,确保原子性和可见性:
    class Model {
        private String code;
        private BigDecimal valueA;
        private BigDecimal valueB;
    
        public synchronized void setCode(String code) { this.code = code; }
        public synchronized void setValueA(BigDecimal valueA) { this.valueA = valueA; }
        public synchronized void setValueB(BigDecimal valueB) { this.valueB = valueB; }
        
        public synchronized String getCode() { return code; }
        public synchronized BigDecimal getValueA() { return valueA; }
        public synchronized BigDecimal getValueB() { return valueB; }
    }
    
  • 不可变Model:每次修改创建新的Model实例,彻底避免并发问题:
    class Model {
        private final String code;
        private final BigDecimal valueA;
        private final BigDecimal valueB;
    
        public Model(String code, BigDecimal valueA, BigDecimal valueB) {
            this.code = code;
            this.valueA = valueA;
            this.valueB = valueB;
        }
    
        // 修改时创建新实例
        public Model update(BigDecimal valueA, BigDecimal valueB) {
            return new Model(this.code, valueA, valueB);
        }
    }
    
    对应的modifyValue方法调整为:
    private static void modifyValue(ConcurrentHashMap<String, Model> v, BigDecimal valueA, BigDecimal valueB, String code) {
        v.compute(code, (k, val) -> {
            if (val == null) {
                return new Model(code, valueA, valueB);
            }
            return val.update(valueA, valueB);
        });
    }
    

关于读作业的说明

ConcurrentHashMap的读操作是弱一致性的,即读作业可能看到旧的映射关系,但如果Model本身是线程安全的(比如用volatile或同步方法),读取到的Model字段会是最新的;如果使用不可变Model,读作业看到的要么是旧实例,要么是新实例,不会出现状态不一致的情况。

内容的提问来源于stack exchange,提问作者SmokingTurtle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 15:33:16