一、基本概念
- 目的:解决多线程并发访问共享资源时的数据竞争问题,保证原子性、可见性和有序性(JMM内存模型)。
- 性质:可重入锁(同一线程可重复获取同一把锁)、独占锁(互斥锁)。
- JVM级别:由JVM实现,无需手动释放锁(编译器自动生成monitorexit指令)
二、三种使用方式
粒度 | 示例 | 锁对象 |
---|---|---|
实例方法 | synchronized void method() | 当前实例 (this) |
静态方法 | synchronized static void method() | 当前类的Class对象(如 Object.class) |
代码块 | synchronized(obj) { … } | 指定对象(任意Object) |
三、底层原理(JVM层面)
-
同步代码块:通过 monitorenter 和 monitorexit 指令实现(编译后插入字节码)。
-
同步方法:方法常量池中设置 ACC_SYNCHRONIZED 标志,调用时隐式获取锁。
-
Monitor机制:
-
每个Java对象关联一个Monitor(管程),包含:
1、 _owner:持有锁的线程
2、_EntryList:阻塞等待锁的线程队列
3、 _WaitSet:调用 wait() 后进入的等待队列
-
线程通过CAS竞争进入 _owner,失败则进入 _EntryList 阻塞。
-
四、锁升级过程(优化重点!)
Java 6后引入自适应自旋锁、锁消除、锁粗化等优化,锁状态存储在对象头Mark Word中。
升级流程:
1、无锁:初始状态
2、偏向锁(Biased Lock)
-
场景:只有一个线程访问
-
原理:在Mark Word存储线程ID,避免CAS操作
3、轻量级锁(Lightweight Lock)
-
场景:多线程交替执行,无实际竞争
-
原理:通过CAS自旋尝试获取锁(避免线程阻塞)
4、重量级锁(Heavyweight Lock)
- 场景:多线程竞争激烈
- 原理:竞争失败线程进入阻塞队列,依赖操作系统Mutex实现
五、 优缺点
优点 | 缺点 |
---|---|
语法简单,自动释放锁 | 无法中断等待锁的线程 |
JVM原生支持,稳定性高 | 非公平锁(可能导致线程饥饿) |
锁升级机制优化低竞争场景性能 | 只能绑定一个条件变量(wait/notify) |
高竞争时升级重量级锁性能下降 |
六、与ReentrantLock对比
特性 | synchronized | ReentrantLock |
---|---|---|
实现 | JVM层面 | JDK API(AQS实现) |
锁中断 | ❌ 不支持 | ✅ lockInterruptibly() |
公平锁 | ❌ 非公平 | ✅ 可选公平/非公平 |
条件变量 | ✅ 单条件 | ✅ 多条件(newCondition()) |
锁超时 | ❌ 不支持 | ✅ tryLock(timeout) |