游乐游手机版
首页/编程语言/文章详情

多线程环境安全读取及替换只读列表方法

时间:2026-07-11 06:43
使用volatile或AtomicReference配合不可变列表,替代synchronized实现无锁读取与原子更新,消除锁竞争,提升读吞吐量。前提是不可变性,需用List copyOf()封装避免并发修改。
本文介绍一种比 synchronized 更轻量、更高性能的线程安全解决方案:利用 volatile 或 AtomicReference 取代传统的同步方法,结合不可变列表(例如 List.copyOf()),实现无锁读取与原子更新操作。

在高并发的生产环境中,频繁使用 synchronized 方法会导致显著的性能损耗和可伸缩性瓶颈。你目前的代码中,getFoo() 和 refresh() 均被 synchronized 修饰,但实际业务场景非常清晰:读取操作不涉及状态修改,写入操作仅整体替换列表——这完全符合“不可变发布”(immutable publication)模型,完全没有必要使用重量级锁。

✅ 推荐方案:volatile + 不可变列表

由于 list 始终通过整体赋值方式更新(不存在原地修改),并且所有读取操作都在赋值完成后进行,volatile 字段足以提供所需的 happens-before 保证:新列表的构建与发布,对所有线程均立即可见。

private volatile List list = List.of(); // 初始化为空不可变列表

public Foo getFoo(Condition condition) {
    // 无需同步:volatile 保证读到最新引用,且列表本身不可变
    for (Foo foo : list) {
        if (condition.check(foo)) {
            return foo;
        }
    }
    return null;
}

public void refresh() {
    // fetchFromDB() 返回新列表;用 List.copyOf() 强制不可变
    List newList = List.copyOf(fetchFromDB());
    list = newList; // volatile 写入,对所有线程立即可见
}

? 注意:List.copyOf()(Java 10+)不仅会创建不可变副本,还会在运行时检查源列表是否被意外修改(抛出 UnsupportedOperationException),因此比 Collections.unmodifiableList() 更加安全可靠。

? 备选方案:AtomicReference>

如果你需要支持更复杂的原子操作(例如 CAS 更新或条件刷新),可以考虑采用 AtomicReference:

private final AtomicReference> listRef = new AtomicReference<>(List.of());

public Foo getFoo(Condition condition) {
    for (Foo foo : listRef.get()) { // get() 具有 volatile 读语义
        if (condition.check(foo)) {
            return foo;
        }
    }
    return null;
}

public void refresh() {
    List newList = List.copyOf(fetchFromDB());
    listRef.set(newList); // set() 具有 volatile 写语义
}

⚠️ 关键注意事项

  • 不可变是核心前提:list 引用所指向的对象必须不可被修改。如果 fetchFromDB() 返回的是可变 ArrayList,必须使用 List.copyOf() 或 Collections.unmodifiableList() 进行封装;否则,其他线程在遍历时可能遇到 ConcurrentModificationException 或数据不一致问题。
  • 元素本身也需要线程安全:Foo 实例自身不应包含共享可变状态;如果内部存在状态,必须确保其线程安全性(例如使用 final 字段、不可变设计或同步访问机制)。
  • 防范隐式修改:切勿将 list 赋值给非 final 的可变变量后再进行修改(例如 List temp = list; temp.add(...)),这将彻底破坏线程安全性。
  • 文档即契约:在字段声明旁添加明确注释:
    // Thread-safe: list is volatile and always points to an unmodifiable List.
    // Never mutate the list or its elements after assignment.
    private volatile List list = List.of();

✅ 总结

相较于 synchronized,volatile 方案彻底消除了锁竞争,显著提升了读取吞吐量;同时结合 List.copyOf() 确保不可变语义,从源头上杜绝了并发修改的风险。该模式简洁、高效、易于测试,堪称“只读+全量更新”场景的最佳实践。

来源:https://www.php.cn/faq/2790437.html
上一篇MapStruct审计字段重复映射注解移除最佳实践 下一篇如何编写高性能代码以配合垃圾回收机制
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
IDEA中SpringBoot项目热部署实现指南
编程语言 · 2026-07-11

IDEA中SpringBoot项目热部署实现指南

Springboot项目在IDEA中实现热部署需依次完成:开启自动编译(静态与动态编译)、启用热部署策略、引入spring-boot-devtools依赖、关闭浏览器缓存,最后启动测试即可生效,省去手动重启时间,大幅提升开发效率。

Java实现Excel转JSON的代码详解
编程语言 · 2026-07-11

Java实现Excel转JSON的代码详解

使用Java结合FreeSpire XLS库可自动将Excel转为JSON,通过读取工作表并映射为键值对,高效支持数据迁移、报表导出与系统对接,大幅提升效率并消除手动录入错误。

Golang高效布谷鸟过滤器多字符集字符串过滤
编程语言 · 2026-07-11

Golang高效布谷鸟过滤器多字符集字符串过滤

布谷鸟过滤器支持删除操作,通过指纹截断与双哈希定位实现多字符集高效过滤。指纹截断需采用fnv64a或xxhash快速哈希并取低8位,桶索引使用独立双哈希避免假阳性。并发安全需以固定数组配合sync atomic实现。

Java使用Poi-tl按Word模板生成动态表格
编程语言 · 2026-07-11

Java使用Poi-tl按Word模板生成动态表格

Poi-tl是Word导出工具,文档周全,支持多级合并表头生成。通过{{text}}、{{?var}}、{{ table}}标签处理模板,传入TableRenderData对象即可动态渲染表格。示例展示用户信息表格生成,并提供工具类消除表格首行缩进,确保格式正确。

Go语言微服务集成Swagger生成交互式API文档完整教程
编程语言 · 2026-07-11

Go语言微服务集成Swagger生成交互式API文档完整教程

在Go微服务中集成Swagger常见四大问题:swaginit初始化失败需确保存在packagemain及注释;SwaggerUI加载失败因路由映射错误或未导入docs;@Param注解必须严格遵循格式;部署后接口文档显示localhost因硬编码host,应删除或通过环境变量动态注入。