Skip to content

[Bug] TimeUtils 中 ThreadLocal 缓存导致 Locale/TimeZone 状态永久滞留 #1849

@QiuYucheng2003

Description

@QiuYucheng2003

描述 Bug

TimeUtils.java 中,为了优化性能使用了 ThreadLocal<Map<String, SimpleDateFormat>> 来缓存时间格式化器。但在执行 new SimpleDateFormat(pattern) 时,实例会隐式依赖并绑定当前线程创建时的系统全局 LocaleTimeZone

在 Android 的多线程环境中,后台工作线程(如线程池)是长期存活且高度复用的。如果用户在应用运行期间切换了系统语言或跨越了物理时区,系统分配的复用旧线程仍会盲目使用 ThreadLocal 中残留的旧格式化器进行渲染。这会导致同一个应用界面上由于多线程执行出现“表现层分裂”(如部分输出法文日期,部分输出英文日期),严重破坏数据一致性和用户体验,且常规代码审查极难察觉。

  • AndroidUtilCode 的版本:- 出现 Bug 的设备型号:- 设备的 Android 版本:## 相关代码

问题代码定位 (TimeUtils.java):

    public static SimpleDateFormat getSafeDateFormat(String pattern) {
        Map<String, SimpleDateFormat> sdfMap = SDF_THREAD_LOCAL.get();
        SimpleDateFormat simpleDateFormat = sdfMap.get(pattern);
        if (simpleDateFormat == null) {
            // 缺陷引爆点:隐式绑定了早期的 Locale 和 TimeZone,且被永久缓存
            simpleDateFormat = new SimpleDateFormat(pattern);
            sdfMap.put(pattern, simpleDateFormat);
        }
        return simpleDateFormat;
    }


修复建议不能仅仅依赖 pattern 作为缓存的 Key建议将缓存的 Key 改为 pattern + Locale.getDefault() + TimeZone.getDefault() 的复合键或者在每次 get() 时校验当前系统的语言/时区是否发生变更如果变更则使当前线程的缓存失效异常堆栈无崩溃堆栈这是一个导致微观内存维度发生静默状态漂移的逻辑缺陷表现为多线程并发下的 UI 渲染错乱和数据格式异常

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions