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

序列化后ConcurrentHashMap为空的原因排查

问题描述

我尝试序列化包含一个已添加10个元素的私有静态ConcurrentHashMap变量的ServletState类。序列化后再反序列化时,其他所有变量均正常,但ConcurrentHashMap变为空。查看ser文件未发现该Map的相关内容,推测序列化配置存在缺失,但未抛出任何异常。

相关代码

ServletState类

public class ServletState implements java.io.Serializable
{
    @Serial
    private static final long serialVersionUID = 1L;

    private static final String serialFileName = "ServletState.ser";

    private static File servletDataPath = new File(YamlReader.getConfig().getApp().getServletDataPath() + serialFileName);

    private static boolean initializeClass = false;

    /**
     * 存储各操作的状态
     */
    private static ConcurrentHashMap<ToolType, ExecuteStatus> servletState = new ConcurrentHashMap<>();
    
    public ServletState()
    {
    }
}

ToolType枚举

public enum ToolType
{
    @JsonProperty("deploy")
    DEPLOY_SERVICES("deployServices"),
    // 其他枚举项
}

Status枚举

/**
 * 存储状态枚举
 */
public enum Status
{
    /**
     * 操作配置已完成
     */
    @JsonProperty("Configured")
    CONFIGURED,
    // 其他枚举项
}

ExecuteStatus类

import java.io.Serializable;
import java.util.Date;

public class ExecuteStatus implements Serializable
{
    @Serial
    private static final long serialVersionUID = 1L;  

    /**
     * 操作类型
     */
    private final ToolType toolType;

    /**
     * 最后执行时间,格式为MM-dd-yyyy hh:mm:ss或N/A
     */
    private final String lastExecuted;

    /**
     * 执行结果状态
     */
    private final Status status;

    public ExecuteStatus(ToolType toolType, Date lastExecuted, Status status) {
        this.toolType = toolType;
        this.lastExecuted = lastExecuted != null ? /* 日期格式化逻辑 */ : "N/A";
        this.status = status;
    }
}
原因分析

Java序列化的核心是序列化对象的实例状态,而非类的静态状态。静态成员变量属于类本身,不属于单个实例,因此默认序列化机制会直接忽略所有静态变量,不会将其写入序列化文件。反序列化时,静态变量会使用类当前的默认值(这里就是初始化的空ConcurrentHashMap),所以会出现Map为空的情况,且这个过程属于序列化机制的正常行为,不会抛出任何异常。

解决方案

根据业务需求,可选择以下方案:

方案1:将静态变量改为实例变量

如果servletState不需要在多个ServletState实例间共享,直接去掉static修饰符,让它成为实例属性:

private ConcurrentHashMap<ToolType, ExecuteStatus> servletState = new ConcurrentHashMap<>();

这样序列化时会正常保存该Map的内容,反序列化后能恢复。

方案2:自定义序列化/反序列化逻辑

如果必须保留servletState的静态特性,可通过Java序列化的钩子方法writeObject和readObject手动处理静态变量的读写:
在ServletState类中添加以下方法:

private void writeObject(ObjectOutputStream out) throws IOException {
    out.defaultWriteObject();
    // 手动写入静态servletState的内容
    out.writeObject(servletState);
}

private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    in.defaultReadObject();
    // 手动读取并恢复静态servletState
    servletState = (ConcurrentHashMap<ToolType, ExecuteStatus>) in.readObject();
}

注意:这种方式下,反序列化时会覆盖类当前的静态servletState值,所有实例都会共享这个恢复后的Map。

方案3:单独持久化静态变量

如果静态变量的状态需要长期保存,也可以将其单独序列化到文件,在类初始化时读取恢复,比如在静态代码块中加载:

static {
    // 从指定文件读取并恢复servletState
    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("servletState.ser"))) {
        servletState = (ConcurrentHashMap<ToolType, ExecuteStatus>) ois.readObject();
    } catch (Exception e) {
        // 加载失败时初始化空Map
        servletState = new ConcurrentHashMap<>();
    }
}

同时在需要保存静态状态时,单独序列化这个Map:

public static void saveServletState() throws IOException {
    try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("servletState.ser"))) {
        oos.writeObject(servletState);
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 14:53:10