在Java中什么是线程安全_Java并发安全核心概念解析

线程安全指多线程并发访问时行为始终符合预期、结果确定且一致;count++因非原子性(load-add-save三步)必然导致数据竞争;synchronized适合简单同步,ReentrantLock支持中断与超时;AtomicInteger仅对单变量原子操作无锁,volatile不保证原子性。

线程安全不是“加了锁就安全”,而是指多个线程并发访问某个类、方法或变量时,**行为始终符合预期,结果确定且一致**。比如 count++ 看似一行,实际分 loadaddsave 三步——只要中间被其他线程插队,结果就错。这不是偶发 Bug,是必然发生的数据竞争。

为什么 count++ 一定不安全?

因为 JVM 不保证其原子性:两个线程可能同时读到 count = 5,各自加 1 后都写回 6,最终丢失一次递增。实测中,两个线程各执行 10000 次 count++,输出常为 12xxx14xxx,而非预期的 20000

  • 根本原因:抢占式调度 + 非原子操作 + 共享可变状态
  • 可见性问题:一个线程改了值,另一个线程可能还缓存在寄存器里,读不到最新值
  • 指令重排序:JVM 或 CPU 可能调整执行顺序,破坏逻辑依赖(如初始化未完成就被其他线程看到)

synchronizedReentrantLock 怎么选?

两者都能互斥执行临界区,但适用场景差异明显:

  • synchronized:自动加锁/解锁,不会忘记释放,适合简单同步(如包装一个计数器、单例 getInstance);但无法中断等待、不支持超时、默认非公平
  • ReentrantLock:需手动 lock()/unlock()(必须在 finally 块中),但支持 tryLock(1, TimeUnit.SECONDS)lockInterruptibly()、公平锁等高级控制,适合高并发+需精细调度的场景
  • 性能上,现代 JVM 对 synchronized 优化极好(偏向锁→轻量级锁→重量级锁),低竞争下甚至比 ReentrantLock 更快

AtomicInteger 就真无锁了吗?

是的,但仅限于它支持的原子操作(如 incrementAndGet()compareAndSet())。它底层靠 CPU 的 CAS 指令实现,没有阻塞、没有上下文切换开销。

  • 适用:单个变量的读-改-写(如计数器、序列号生成)
  • 不适用:需要原子地更新多个变量,或涉及复杂逻辑(如 “先查库存再扣减再记录日志”)——这时 CAS 无法覆盖整个业务单元,仍得用锁
  • 注意:getAndIncrement()incrementAndGet() 返回值不同,别混淆

volatile 能替代锁吗?

不能。它只解决**可见性**和**禁止重排序**,不解决**原子性**。

  • 典型误用:volatile boolean flag + flag = !flag —— 这仍是读+写两步,多线程下会出错
  • 正确用法:状态标志(如 running)、双重检查单例中的 instance 引用(配合 final 字段保证构造完成可见)
  • 记住:volatile 是“轻量级同步”,不是“轻量级锁”

真正难的从来不是“怎么加锁”,而是判断“哪里需要同步”“哪些状态该共享”“哪些其实可以隔离”。比如用 ThreadLocal 避免共享、用不可变对象(final 字段+无 se

tter)彻底消除风险——这些设计决策,比写对一个 synchronized 块影响更深远。