线程安全取决于对象状态是否被并发修改及访问是否受控,而非引用类型本身;共享可变对象易引发竞态条件,不可变对象天然线程安全,ThreadLocal提供线程局部副本规避竞争,同步机制和并发工具保障共享可变对象的安全访问。

引用类型本身不决定线程安全,真正起作用的是它所指向的对象是否被多个线程并发修改,以及访问方式是否受控。
共享可变对象是风险源头
如果多个线程持有同一个对象的引用,并且都可能调用其非线程安全的修改方法(比如 ArrayList.add()、HashMap.put()),就极易出现数据错乱、丢失或抛出 ConcurrentModificationException。这不是引用类型的问题,而是对象状态被并发写入导致的竞态条件。
例如:
- 两个线程同时对一个未加锁的 StringBuilder 调用 append(),结果可能缺失部分字符;
- 多个线程往同一个 ArrayList 中添加元素,最终 size 可能小于预期,甚至引发数组越界异常。
不可变对象天然线程安全
引用指向一个不可变对象(如 String、Integer、LocalDateTime 或自定义 final 字段+无修改方法的类),即使多个线程共享该引用,也不会有线程安全问题——因为对象状态无法被改变。
关键点在于:不可变性 = 线程安全的充分非必要条件。只要对象创建后状态永远不变,任何线程读取都是安全的。
线程局部副本可规避竞争
使用 ThreadLocal<T> 为每个线程提供独立的对象实例,本质上是让“同一个引用类型变量”在不同线程中指向**不同的对象**。这样既复用了对象创建逻辑,又彻底消除了共享和竞争。
典型场景包括:
- 每个线程独享一个 SimpleDateFormat(因其非线程安全);
- Web 应用中保存当前请求的 UserContext 或数据库连接;
- 避免频繁创建开销大的对象,同时保证隔离性。
同步与并发工具决定行为边界
当必须共享可变对象时,线程安全性取决于访问控制机制:
- synchronized 或 ReentrantLock:串行化临界区操作,适合复杂逻辑或跨多字段协调;
- java.util.concurrent 容器(如 ConcurrentHashMap、CopyOnWriteArrayList):内部已做并发优化,适合高频读+低频写的场景;
- AtomicReference 及其子类:适用于需要 CAS 原子更新整个对象引用的场景(如无锁栈、状态机切换)。
注意:仅把引用声明为 final 并不能保证其指向对象的线程安全,它只确保引用本身不可重赋值。

















