当前位置: 首页 > 图灵资讯 > 技术篇> Java缓存数据读取失败:静态变量与单例模式的陷阱及解决方案?

Java缓存数据读取失败:静态变量与单例模式的陷阱及解决方案?

来源:图灵教育
时间:2025-03-14 16:20:00

java缓存数据读取失败:静态变量与单例模式的陷阱及解决方案?

Java缓存数据读取失败:避免静态变量和单例模式的陷阱

在Java应用程序中,缓存大量数据以提高性能是很常见的。然而,有时会出现从缓存中读取数据失败的问题。本文分析了一个案例,讨论了Java缓存数据读取失败的原因,并提供了解决方案。

案例:内存不足导致缓存数据丢失

开发人员使用scenariobuffer类将大约16万条资产数据加载到名为assetbuffer的HashMap中。用于读取缓存数据的getbasset方法。当服务器内存不足(可用内存只剩下100MB,缓存占用3GB,总内存为8GB)时,getbasset方法返回空值。重新启动服务器并清除缓存后,问题得到解决。使用tomcat启动项目,分配约3GB内存。代码如下:

立即学习“Java免费学习笔记(深入);

@Component
@Order(1)
@Slf4j
public class scenariobuffer implements IActionListener, ApplicationRunner {
    private static Map<String, List<Asset>> assetbuffer = Collections.synchronizedMap(new HashMap<>());
    private static scenariobuffer instance = new scenariobuffer();

    public static scenariobuffer getinstance() {
        return instance;
    }

    public static List<Asset> getbasset(String groupid) {
        if (assetbuffer.containsKey(groupid)) {
            return assetbuffer.get(groupid);
        }
        return null;
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        IAssetService assetService = SpringUtil.getBean(IAssetService.class);
        List<Asset> assetlist = assetService.list();
        assetbuffer.put("key", assetlist);
    }
}

问题分析及解决方案

问题不是简单的内存不足导致JVM清空数据,而是代码设计的缺陷:

  1. static和getinstance()使用不当: scenariobuffer类使用static关键字,使其成为单例。但是getinstance()的方法是冗余的,因为spring容器保证@component注释的bean是单例的。static关键字导致assetbuffer成为静态变量,生命周期与应用服务器相同,即使JVM垃圾被回收,也很难回收其内存。

  2. Bean获取方法不推荐: 使用SpringUtill使用Spring.getBean(IAssetService.class)获取Bean,不推荐。建议使用@Autowired或@resource注释来依赖注入Spring框架。

  3. 初始化时机问题: 应用启动时使用Applicationrunner接口进行初始缓存,但更好的方法是使用@postconstruct注解或实现initializingbean接口,使初始化过程更加清晰可控。

改进后的代码

修改scenariobufer类及其使用方法:

@Component
public class scenariobuffer implements IActionListener {
    private Map<String, List<Asset>> assetbuffer = new HashMap<>();

    @Autowired
    private IAssetService assetservice;

    @PostConstruct
    public void init() {
        List<Asset> assetlist = assetservice.list();
        assetbuffer.put("key", assetlist);
    }

    public List<Asset> getbasset(String groupid) {
        return assetbuffer.get(groupid);
    }
}

使用@Resource注释注入scenariobuffer的其它服务类别:

@Service
public class XxxService {
    @Resource
    private ScenarioBuffer scenarioBuffer;

    public void xxx() {
        List<Asset> asset = scenarioBuffer.getBAsset("xxx");
    }
}

改进后,避免了不必要的静态变量和单例访问方法,使用Spring框架依赖注入机制和@Postconstruct注释,使代码更清晰、更容易维护,降低了内存泄漏的风险。即使服务器内存紧张,JVM垃圾回收机制也能更有效地工作,减少缓存数据读取失败的可能性。

以上是Java缓存数据读取失败:静态变量和单例模式的陷阱和解决方案?详情请关注图灵教育的其他相关文章!