第一部分:synchronized基础
理解synchronized关键字在多线程编程中的核心作用
学习目标
掌握synchronized的作用、解决的问题以及基本使用场景
同步问题
多线程环境下,当多个线程同时访问共享资源时,如果没有同步机制,会导致:
- 数据不一致
- 竞态条件
- 不可预知的结果
synchronized作用
synchronized关键字提供了一种内置的锁机制,用于:
- 保证操作的原子性
- 保证内存可见性
- 防止指令重排序
无同步时,两个线程同时读取和修改共享变量,导致最终结果错误
使用synchronized后,线程顺序执行共享资源操作,保证结果正确

synchronized保证同一时间只有一个线程可以执行同步代码块
第二部分:synchronized使用方式
掌握synchronized的三种使用方式及适用场景
学习目标
理解实例方法同步、静态方法同步和同步代码块的区别与应用
1. 实例方法同步
锁对象是当前实例对象(this)
适用场景: 保护实例变量,防止多个线程同时访问同一个实例的方法
2. 静态方法同步
锁对象是当前类的Class对象
适用场景: 保护静态变量,防止多个线程同时访问类的静态方法
3. 同步代码块
可以指定任意对象作为锁,提供更细粒度的控制
适用场景: 需要更细粒度的锁控制,减少锁的竞争范围
同步方式 | 锁对象 | 作用范围 | 性能影响 |
---|---|---|---|
实例方法同步 | 当前实例对象(this) | 单个实例 | 较高(锁整个方法) |
静态方法同步 | 类的Class对象 | 所有实例 | 高(全局锁) |
同步代码块 | 任意指定对象 | 代码块范围 | 较低(细粒度控制) |
使用建议
1. 优先使用同步代码块,减少锁的范围
2. 避免使用对象锁保护静态变量
3. 使用private final对象作为锁对象
4. 避免在锁代码块中调用外部方法(防止死锁)
第三部分:synchronized工作原理
深入理解synchronized的底层实现机制
学习目标
掌握监视器锁(monitor)机制、对象头结构和内存语义
监视器锁(Monitor)
synchronized基于监视器锁实现,每个Java对象都与一个监视器关联
- 进入同步代码块时自动获取锁
- 退出同步代码块时自动释放锁
- 基于对象头的Mark Word实现
对象头结构
Java对象在内存中的布局:
- Mark Word(标记字段)
- Klass Pointer(类型指针)
- Instance Data(实例数据)
- Padding(对齐填充)
synchronized使用Mark Word存储锁信息
synchronized内存语义
synchronized保证内存可见性和有序性:
- 进入同步块前:从主内存刷新变量值
- 退出同步块时:将修改刷新到主内存
- 防止编译器和处理器重排序
synchronized保证1和2不会被重排序,3和4不会被重排序,且线程B能看到线程A的所有修改
第四部分:synchronized锁升级
理解Java 6引入的锁优化机制
学习目标
掌握偏向锁、轻量级锁、重量级锁的升级过程
偏向锁
适用于只有一个线程访问同步块的场景
- 锁对象记录线程ID
- 同一线程进入无需CAS操作
- 减少同步开销
轻量级锁
适用于线程交替执行的场景
- 使用CAS操作获取锁
- 避免线程阻塞
- 自旋优化
重量级锁
适用于多线程竞争的场景
- 使用操作系统的互斥量
- 线程阻塞进入等待队列
- 开销最大
锁升级过程
1. 初始状态:无锁
2. 第一个线程访问:升级为偏向锁
3. 第二个线程访问:升级为轻量级锁(自旋)
4. 自旋超过阈值(默认10次):升级为重量级锁
5. 重量级锁使用操作系统互斥量,线程进入阻塞状态
注意事项
1. 锁升级不可逆
2. 偏向锁在竞争激烈时反而降低性能
3. 可通过JVM参数调整锁行为(如-XX:-UseBiasedLocking)
4. 自旋锁消耗CPU,需合理设置自旋次数
第五部分:常见错误与陷阱
避免synchronized使用中的常见问题
1. 锁对象错误
错误地使用可变对象或基本类型作为锁
锁对象必须是final不可变对象,避免使用String常量池对象
2. 锁范围过大
同步整个方法或大段代码,降低并发性能
解决方案:使用同步代码块,只保护真正需要同步的部分
3. 死锁问题
多个线程互相持有对方需要的锁
解决方案:按固定顺序获取锁,使用tryLock()尝试获取锁
4. 锁粒度不当
过粗或过细的锁粒度影响性能
- 锁粒度过粗:降低并发度
- 锁粒度过细:增加锁开销
解决方案:根据业务场景选择合适粒度的锁
5. 忘记释放锁
虽然synchronized会自动释放锁,但异常时需注意
解决方案:在finally块中确保释放资源(Lock接口),synchronized会自动释放
6. 锁对象不一致
使用不同对象保护同一资源
必须使用同一个锁对象保护同一资源
第六部分:synchronized最佳实践
高效、安全地使用synchronized
synchronized优点
- 简单易用,语法简洁
- 自动释放锁,避免忘记释放
- JVM内置优化(锁升级)
- 保证内存可见性和有序性
synchronized缺点
- 功能有限(不可中断、不可超时)
- 性能在竞争激烈时下降
- 无法实现公平锁
- 锁粒度控制不如Lock灵活
最佳实践1:选择合适的锁对象
- 使用private final对象作为锁
- 避免使用字符串常量或基本类型
- 静态同步使用Class对象
最佳实践2:减小锁范围
- 优先使用同步代码块而非同步方法
- 只保护真正需要同步的代码
- 避免在同步块中执行耗时操作
最佳实践3:避免嵌套锁
- 尽量避免在同步块内获取其他锁
- 必须嵌套时,按固定顺序获取锁
- 使用tryLock()尝试获取锁
性能优化建议
1. 减少锁持有时间
2. 降低锁粒度(如分段锁)
3. 读写分离(ReadWriteLock)
4. 无锁编程(原子类)
5. 避免热点锁(锁竞争激烈)
第七部分:练习与测验
通过练习巩固synchronized知识
动手练习
- 实现一个线程安全的计数器
- 使用synchronized解决生产者-消费者问题
- 实现一个线程安全的LRU缓存
- 使用同步代码块优化一个同步方法
synchronized知识测验
深入学习资源
1. 《Java并发编程实战》第2章、第13章
2. Oracle官方文档:Java同步机制
3. JVM规范:synchronized实现原理
4. OpenJDK源码:ObjectMonitor实现