synchronized锁升级
无锁->偏向锁
当一个对象刚实例化时,没有线程来访问它,这时这个对象是可偏向的。当第一个线程来访问它时,线程查看该对象的Mark Word(对象头里的数据结构)里是否有指向自己的线程id。这时没有,线程会使用CAS操作修改Mark Word的状态为偏向锁并且将Mark Word的线程id指向自己。线程下一次来访问时,检查到有自己的id就可以直接使用了,使用偏向锁可以节省CAS时间。
偏向锁->轻量级锁
当第二个线程来访问时,也会检查有没有自己的线程id,也会去CAS操作尝试去修改Mark Word。等待锁指向的线程到达全局安全点,暂停锁指向的线程,查看线程是否还活着。如果挂了或者没有在使用该锁了,就可以将对象的线程id修改为指向自己(第二个线程)的线程id,这样仍然是偏向锁。如果还在使用锁,则偏向锁升级为轻量级锁,此时执行第一个线程,在栈桢中建立存储锁记录的空间,将Mark Word复制到这个空间,然后CAS操作将Mark Word替换为指向这个空间的指针。升级为轻量级锁后,第一个线程继续执行同步代码,第二线程进入自旋尝试获取锁。轻量级锁通过自旋来避免线程上下文切换的开销,但会占用CPU。
当第一个线程执行完同步代码,将释放轻量级锁,通过CAS操作将栈桢中的Mark Word复制对象替换回对象头。操作成功则仍然是轻量级锁。
轻量级锁->重量级锁
当锁为轻量级锁时,自旋超过一定次数,或者出现一个线程占有锁,一个线程在自旋,还有第三个来访者时,锁升级为重量级锁(修改Mark Word为指向重量级锁的指针,这样轻量级锁的持有线程释放时的CAS操作就会失败),这时除了拥有锁的线程,其他竞争锁的线程阻塞,防止CPU空转。
重量级锁通过对象的监视器(Monitor)实现,Monitor本质量依赖于操作系统的Mutex Lock实现。JVM基于进入和退出Monitor对象来实现同步。