1. 同步的本质与并发困境
当多个线程同时访问同一块内存区域时,就会出现竞态条件(Race Condition)。我曾在生产环境遇到过这样一个案例:一个简单的计数器在多线程环境下出现了数值错乱,最终统计结果比实际少了17%。这就是典型的线程安全问题——非原子性操作被线程调度打断导致的。
Java内存模型(JMM)规定了所有变量都存储在主内存中,每个线程有自己的工作内存。线程对变量的操作必须经过"读取主内存→修改工作内存→写回主内存"的过程。假设两个线程同时执行i++操作:
- 线程A读取主内存i=0到工作内存
- 线程B也读取i=0到工作内存
- 线程A执行i+1=1
- 线程B执行i+1=1
- 线程A将1写回主内存
- 线程B将1写回主内存
最终i的值是1而非预期的2。synchronized正是为了解决这类问题而生的同步原语。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. synchronized的底层实现机制
2.1 对象头与Monitor结构
每个Java对象在堆内存中都由三部分组成:对象头、实例数据和填充对齐。对象头中的Mark Word(32位系统占4字节)就存储着锁状态信息。当使用synchronized时:
- 无锁状态:存储对象的hashCode和分代年龄
- 偏向锁:存储持有偏向锁的线程ID
- 轻量级锁:指向栈中锁记录的指针
- 重量级锁:指向互斥量(Monitor)的指针
Monitor是真正的同步机制实现,包含以下关键字段:
java复制ObjectMonitor() {
_header = NULL; // 对象头
_count = 0; // 重入次数
_waiters = 0; // 等待线程数
_recursions = 0; // 递归计数
_object = NULL; // 关联的对象
_owner = NULL; // 持有线程
_WaitSet = NULL; // 等待队列(wait()的线程)
_WaitSetLock = 0;
_EntryList = NULL; // 阻塞队列(等
