线程安全设计需精准识别可变共享状态并匹配保护策略:优先用不可变设计;若必须可变,则依场景选细粒度锁、synchronized、volatile或原子类;严防对象逸出与不安全发布。

设计线程安全的类,核心是控制多个线程对共享状态的并发访问。关键不在于“加锁越多越安全”,而在于精准识别可变共享状态,并选择匹配其使用场景的保护策略。
明确哪些状态需要保护
不是所有字段都需要同步。只关注被多个线程同时读写的可变共享变量:
- 静态字段(类级别共享)
- 实例字段(多个线程操作同一对象实例时)
- 通过参数、返回值或内部引用暴露出去的可变对象(如传入的ArrayList、返回的集合引用)
局部变量、方法参数(非逃逸)、不可变对象(String、Integer、自定义final类)天然线程安全,无需额外处理。
优先用不可变(Immutable)设计
如果类的状态在创建后不需要改变,这是最简洁、最高效的安全方式:
立即学习“Java免费学习笔记(深入)”;
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 类声明为final,防止子类破坏不可变性
- 所有字段用private final修饰
- 构造器完成全部初始化;若字段是可变对象(如List),必须做防御性拷贝
- 只提供getter,且对集合类也返回新副本(如
new ArrayList(internalList))
例如:ImmutablePerson类没有setter,每次“修改”都返回新实例,多线程读取完全无冲突。
对可变状态选择合适同步机制
当状态必须可变时,根据粒度、性能和语义选择:
-
细粒度锁:用专用的
private final Object lock = new Object()保护特定字段,避免锁整个方法 - synchronized方法:适合逻辑简单、调用频次低的场景;注意它锁的是当前实例(或Class对象),不是字段本身
-
volatile:仅适用于状态标志(如
isRunning)、单次写入后只读的发布场景;它不保证复合操作(如i++)的原子性 -
原子类:如
AtomicInteger、AtomicReference,适合计数、状态切换等简单操作,底层用CAS,无锁但有重试开销
警惕对象逸出与不安全发布
即使类本身设计得当,错误的构造过程也会导致线程安全失效:
- 构造器中不要将
this引用传递给外部(如注册监听器、启动线程),此时对象可能尚未初始化完毕 - 静态单例必须确保懒加载时的双重检查锁定(DCL)正确实现,且instance字段需用
volatile修饰,防止指令重排序 - 避免直接返回内部可变集合的引用;getter中应返回副本或使用
Collections.unmodifiableList
本质上,线程安全不是靠某一个关键字堆砌出来的,而是从对象生命周期、状态可见性、操作原子性三个维度系统设计的结果。

















