synchronized经过多个jdk版本的优化,性能已经得到了巨大的提升,但是在某些部分,比如说使用的灵活性上可能不如ReentrantLock或者ReentrantReadWriteLock等一些并发包提供的锁实现,还有一个需要注意的点儿是synchronized存在一个锁升级的过程,而且这个升级过程是不可逆的,也就是说经过升级后,所有锁都膨胀为重量级锁,那么系统的整体性能会受到影响。
    sync升级的过程简单概括为偏向锁——轻量级锁——重量级锁。之所以存在锁升级的过程,主要思想是并发的程序在微观上大部分时间其实并不是时刻都在竞争,所以,并不需要每次操作数据都去进行互斥,从而优化锁的性能。首先梳理一下各个锁的含义:

    • 偏向锁:主要是为了消除数据在无竞争状态下的同步原语,即CAS操作,相对轻量级锁在无竞争情况下是使用CAS操作来消除同步使用的互斥量,那么偏向锁就是在无竞争情况下把整个同步原语也消除掉,连CAS操作也不做了
    • 轻量级锁:轻量级锁不是用来替代重量级锁的,本意是在没有多线程的竞争下,减少重量级锁使用操作系统来操作互斥量带来的性能消耗。轻量级锁不一定必重量级锁效率更高,在并发量高的情况下,轻量级锁相对于重量级锁,除了互斥带来的性能消耗,还有CAS操作带来的消耗。
    • 重量级锁:通过操作系统互斥量来实现,获取不到锁会阻塞

    sync的整个升级过程依靠的是对象头Mark Word,Mark Word主要由两部分组成,第一部分用来存储对象自身运行时数据,如哈希码,GC分代年龄,这一部分是实现轻量级锁和偏向锁的关键,另一部分是用于存储指向方法区对象类型的数据指针。
    偏向锁的过程是线程在获取锁对象时,先看一下Mark Word中是否记录了线程id,如果没有,将自己的线程id通过CAS操作防如Mark Word中。偏向状态下,持有锁的线程是可以重复进入的。如果这是另一个线程也去竞争锁,发现对象头上已经有了其他的线程id,那么会检查持有该锁的线程是否存活,如果线程结束,则修改对象为无锁状态,然后从新执行偏向或所的过程,如果线程仍然存活,则升级为轻量级锁。
    image.png
    轻量级锁的过程是线程将通过CAS操作将对锁对象的Mark Word内容复制到线程的锁记录中,并将对象头的Mark Word更新为指向锁记录的指针。
    获锁步骤:

    1. 在代码进入同步块的时候,如果同步对象锁状态为无锁状态(锁标志位为“01”状态,是否为偏向锁为“0”),虚拟机首先将在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的Mark Word的拷贝,官方称之为 Displaced Mark Word
    2. image.png
    3. 拷贝对象头中的Mark Word复制到锁记录(Lock Record)中
    4. 拷贝成功后,虚拟机将使用CAS操作尝试将锁对象的Mark Word更新为指向Lock Record的指针,并将线程栈帧中的Lock Record里的owner指针指向Object的 Mark Word。
    5. 如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象Mark Word的锁标志位设置为“00”,即表示此对象处于轻量级锁定状态
    6. image.png
    7. 如果这个更新操作失败了,虚拟机首先会检查对象的Mark Word是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行。否则说明多个线程竞争锁,轻量级锁就要膨胀为重量级锁,锁标志的状态值变为“10”,Mark Word中存储的就是指向重量级锁(互斥量)的指针,后面等待锁的线程也要进入阻塞状态

    image.png