不可变对象天然线程安全,需同时满足:类为final、字段private final、字段类型不可变或经防御性拷贝、构造器内完成校验与防逸出、状态变更返回新实例。

Java 多线程环境下,不可变对象能天然实现线程安全——不是靠锁或同步,而是靠“创建即定型”的设计约束,彻底消除写操作和状态竞争。
核心条件必须全部满足
一个真正线程安全的不可变对象,需同时满足以下五点:
- 类声明为 final,防止子类通过继承添加可变字段或重写方法破坏契约
- 所有字段声明为 private final,禁止外部访问与重新赋值
- 字段类型本身必须不可变:优先用
String、Integer、LocalDateTime、BigDecimal等 JDK 内置不可变类型 - 若含集合类(如
List、Map),必须使用不可变封装:Collections.unmodifiableList()、List.copyOf()(Java 10+)或ImmutableList.of()(Guava) - 构造器内完成全部校验与防御性拷贝,禁止
this引用逸出(例如不传this给静态容器、不启动线程、不注册监听器)
特别注意可变组件的“逃逸”风险
仅把字段标为 final 不等于安全。常见误区是:
-
private final List<string> items;</string>—— 引用不可变,但列表内容仍可被外部修改 - 传入
Date或ArrayList时未做拷贝,原始对象可能在别处被改
正确做法是:接收可变参数时立即深拷贝,对外提供不可变视图。例如:
public List状态变更要靠“新建”,而非“修改”
不可变对象不提供 setter,而是返回新实例来表达演进逻辑:
- 用语义化方法替代修改:如
OrderSnapshot withStatus(OrderStatus newStatus) - 配合
record(Java 14+)可大幅简化:自动具备final字段、不可变性、equals/hashCode/toString - 避免高频全量重建:适用于读远多于写的场景,如配置快照、事件对象、API 响应 DTO
String 是最典型的不可变安全范例
它印证了不可变设计如何支撑并发稳定性:
- 内部
private final byte[] value(JDK 9+)无法被 public 方法修改 - 所有
concat、replace、substring都返回新对象,原实例恒定 - 作为
ConcurrentHashMap的 key 安全可靠,哈希值缓存有效,不会“丢失” - 静态常量如
public static final String DB_URL = "..."可被任意线程直接共享,零同步开销
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











