synchronized底层原理+锁升级
一、底层原理
- 同步代码块:通过
monitorenter
和monitorexit
两条字节码指令来实现,本质是获取对象的Monitor(监视器锁)
- 同步方法:通过方法上的
ACC_SYNCHRONIZED
访问标志来标识,当方法被调用时,调用指令会检查该标志,然后自动执行加锁和解锁的过程
二、Monitor
Monitor
是线程私有的数据结构,每一个java对象潜在的与一个Monitor相关联,在java对象头中存在一个指向Monitor的指针。
Monitor
主要包含的组件:
owner
:指向持有该monitor的线程,初始时为nullentryList
:处理BLOCKED
阻塞状态的线程,正在等待锁释放,以竞争所有权waitSet
:处于Waiting
状态的线程,这些线程执行了object.wait()
方法,等待被notify() / notifyAll()
唤醒
工作流程:
- 线程执行到
monitorenter
指令时,会尝试占用该对象的Monitor
- 如果
owner
为null,当前线程成为owner
,并将计数器置为1 - 如果当前线程已经是
owner
,且是可重入锁,则重入此锁,计数器加1 - 如果
owner
是其它的线程,当前线程进入到entryList
中,变成BLOCKED
状态 - 持有
Monitor
的线程执行完同步代码块后,会执行monitorexit
指令,将计数器减1。当计数器减为0时,线程释放Monitor
,并唤醒entryList
中等待的线程来重新竞争锁
Monitor
机制是依赖于操作系统的 Mutex Lock
互斥锁来实现的,涉及到用户态到内核态的切换,进程需要切换上下文,成本非常高,所以 synchronized
是一个重量级锁。
三、锁升级
为减少获取锁和释放锁带来的性能消耗,synchronized
的锁是不断升级的,而不是一开始就是重量级锁,锁只能升级不能降级。锁的级别从低到高为:
无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁
升级过程:
无竞争时,使用偏向锁,该线程后续进入和退出同步块时,发现记录的偏向的线程id是自身,则直接进入。当发现有其它线程来竞争锁时,偏向锁撤销,升级为轻量级锁;
轻量级锁时,通过CAS
自旋来加锁和解锁,避免线程阻塞。CAS
操作失败且之前加锁的不是自身,说明有多个线程在竞争,这时候会升级为重量级锁,也就是Monitor
机制