【锁的本质】锁本身就是共享资源,那么锁如何保证线程安全?

锁本身作为共享资源却能保证线程安全,以及AtomicInteger等原子类的线程安全实现,其根本原因都依赖于硬件层面的原子操作指令内存屏障。下面分层解析其原理:


一、锁如何保证自身线程安全(以 ReentrantLock 为例)

1. 底层依赖:CAS (Compare-And-Swap)

java 复制代码
// AQS 中的核心 CAS 操作(简化版)
protected final boolean compareAndSetState(int expect, int update) {
    // 调用 Unsafe 类的 CAS 原子操作
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}
  • 硬件支持 :CPU 提供 LOCK CMPXCHG 等原子指令(x86 架构)
  • 操作原子性:比较-交换操作在一条指令内完成,不会被线程切换打断

2. 锁获取流程(非公平锁)

java 复制代码
final void lock() {
    if (compareAndSetState(0, 1))  // 原子操作:尝试将 state 从 0 改为 1
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

关键保障

  1. 多个线程同时执行 CAS 时,只有一个线程 能成功修改 state
  2. 失败线程进入 CLH 队列等待(通过 CAS 保证入队操作的线程安全)

3. 操作系统级保障

当竞争激烈时(重量级锁):

c++ 复制代码
// HotSpot 源码中的锁膨胀(linux 实现)
void ObjectSynchronizer::inflate(Thread* self, oop object) {
    ...
    // 创建 ObjectMonitor 对象
    ObjectMonitor* monitor = new ObjectMonitor();
    // 通过 CAS 将对象头指向 monitor
    if (Atomic::cmpxchg_ptr(monitor, object->mark_addr(), mark) == mark) {
        ...
    }
}
  • 最终依赖操作系统提供的 mutex 互斥量 (如 Linux 的 pthread_mutex_t
  • 通过系统调用(如 futex)实现线程阻塞/唤醒

二、AtomicInteger 的线程安全原理

1. 核心实现(JDK 源码)

java 复制代码
public class AtomicInteger {
    private volatile int value;
    
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
}

// Unsafe 类中的关键操作
public final int getAndAddInt(Object o, long offset, int delta) {
    int v;
    do {
        v = getIntVolatile(o, offset); // 读取当前值
    } while (!compareAndSwapInt(o, offset, v, v + delta)); // CAS 重试
    return v;
}

2. 关键保障机制

机制 作用 实现层级
volatile 变量 保证内存可见性(禁止指令重排序) JVM 内存屏障
CAS 操作 保证修改的原子性 CPU 硬件指令
自旋重试 处理竞争失败 Java 代码层

三、对比 Integer 的线程不安全

1. Integer 的自增操作

java 复制代码
Integer count = 0;
count = count + 1; // 非原子操作

实际执行步骤:

  1. 读取当前值 count (非原子)
  2. 计算 count+1 (非原子)
  3. 写入新值 (非原子)

2. 多线程下的问题

java 复制代码
// 线程 A          |  // 线程 B
read count=0       |
                   | read count=0
calc 0+1=1         |
                   | calc 0+1=1
write count=1      |
                   | write count=1  // 结果应为2,实际为1

四、根本原因对比

特性 锁 (ReentrantLock) 原子类 (AtomicInteger) Integer
原子性保障 CAS+队列管理 CAS 循环
可见性保障 AQS 的 volatile state volatile 变量
竞争处理 队列阻塞 CPU 自旋 -
硬件依赖 CPU 原子指令+OS 系统调用 CPU 原子指令
适用场景 复杂同步逻辑 单一变量原子操作 只读/线程局部变量

五、硬件与操作系统协作示意图

复制代码
+---------------------+     +---------------------+
|     Java 代码层      |     |       JVM 层         |
|   - ReentrantLock   |<--->| - AQS 实现          |
|   - AtomicInteger   |     | - 锁膨胀机制         |
+----------↑----------+     +----------↑----------+
           |                           |
+----------↓----------+     +----------↓----------+
|  本地方法库 (JNI)   |     |     操作系统层        |
|   - Unsafe 类       |<--->| - mutex 互斥量        |
|   - CAS 操作        |     | - futex 快速锁        |
+----------↑----------+     +----------↑----------+
           |                           |
+----------↓----------+     +----------↓----------+
|        CPU 层       |     |       内存子系统      |
|  - LOCK 指令前缀    |<--->| - 缓存一致性协议      |
|  - CMPXCHG 指令     |     | - 内存屏障           |
+---------------------+     +---------------------+

六、关键结论

  1. 锁的自身安全根本原因

    • 硬件原子指令(CAS)保证状态修改的原子性
    • 内存屏障保证状态可见性
    • 操作系统提供阻塞/唤醒原语
  2. 原子类的线程安全根本原因

    • volatile 保证可见性
    • CAS 循环保证原子修改
    • 无锁设计避免上下文切换
  3. Integer 线程不安全原因

    • 复合操作(读-改-写)不具备原子性
    • 缺乏内存可见性保障

💡 设计启示

  • 简单原子操作优先使用原子类(如 AtomicInteger
  • 复杂同步逻辑使用锁(如 ReentrantLock
  • 避免在无同步下修改共享基本类型(如 int
相关推荐
九皇叔叔20 小时前
MySQL 意向锁为什么不会阻塞行锁之间的并发?
数据库·mysql··意向锁
egoist20234 天前
[linux仓库]线程池(单例模式)、线程安全与重入、死锁[线程·拾]
linux·单例模式·饿汉模式·懒汉模式·线程安全·死锁·重入问题
没有bug.的程序员8 天前
Java锁优化:从synchronized到CAS的演进与实战选择
java·开发语言·多线程·并发·cas·synchronized·
lkbhua莱克瓦2415 天前
进阶-锁章节
数据库·sql·mysql·oracle·存储过程·
_OP_CHEN18 天前
【从零开始的Qt开发指南】(二十)Qt 多线程深度实战指南:从基础 API 到线程安全,带你实现高效并发应用
开发语言·c++·qt·安全·线程·前端开发·线程安全
予枫的编程笔记18 天前
【Java进阶】深入浅出 Java 锁机制:从“单身公寓”到“交通管制”的并发艺术
java·人工智能·
小毅&Nora20 天前
【Java线程安全实战】⑨ CompletableFuture的高级用法:从基础到高阶,结合虚拟线程
java·线程安全·虚拟线程
C++chaofan23 天前
Java 并发编程:synchronized 优化原理深度解析
java·开发语言·jvm·juc·synchronized·
就这个丶调调1 个月前
Java ConcurrentHashMap源码深度解析:从底层原理到性能优化
java·并发编程·源码分析·线程安全·concurrenthashmap
小毅&Nora1 个月前
【Java线程安全实战】③ ThreadLocal 源码深度拆解:如何做到线程隔离?
线程安全·threadlocal·jdk源码