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

Tomcat缓存源码CachedResource的getLastModified方法写法疑问

关于CachedResource.getLastModified()两种写法的差异说明

直接操作成员变量的写法存在并发安全问题

你改写的第二种实现,在多线程并发场景下会随机抛出空指针异常,核心原因是代码会两次读取堆上的成员变量cachedLastModified,两次读取之间没有任何一致性保证:

  1. 第一次读取发生在if (this.cachedLastModified == null)判空逻辑中
  2. 第二次读取发生在return this.cachedLastModified.longValue()返回逻辑中

CachedResource本身带缓存失效机制,缓存到期时后台清理线程会把cachedLastModified重置为null触发下次重载;同时这个方法没有加任何同步锁,cachedLastModified字段也没有用volatile修饰,两次读取之间完全可能被其他线程修改值:如果第一次判空时读到非null值,跳过了赋值分支,结果返回前第二次读取刚好读到清理线程设置的null值,对null调用longValue()就会直接触发NPE。

Tomcat采用局部变量缓存写法的设计考量

Tomcat的写法本质是Java高并发无锁编码中非常经典的局部变量快照技巧,核心考量有两点:

  • 彻底避免空指针风险:代码只在方法最开始读取一次成员变量,把引用拷贝到线程私有的栈上局部变量中,后续所有判空、赋值、返回操作都基于这个局部变量完成。不管其他线程怎么修改堆上的成员变量,当前线程持有的局部变量引用都不会变,只要第一次读取拿到非null值,后续调用longValue()就绝对不会触发NPE。
  • 性能最优:这种实现完全不需要加锁,也不需要给字段加volatile带来内存屏障开销,同时局部变量的栈读写速度远快于堆成员变量读写。Tomcat刻意允许极端场景下多个线程同时判空、重复给cachedLastModified赋值——因为webResource.getLastModified()的返回值是幂等的,重复赋值不会产生业务错误,用极低概率的重复计算换无锁的高吞吐,是Web容器这类基础组件非常典型的工程取舍。

内容的提问来源于stack exchange,提问作者S. Pan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 05:21:23