(1)synchronized
同步代码块,当锁住一个object的时候,有个锁升级的过程。
1. 偏向锁:
对象头的偏向锁字段会记录占用该锁的线程ID,当下次相同的线程来的时候,就直接获锁。不用进行CAS的操作。
升级过程
由于偏向锁不会主动释放锁。
a.当线程进入的时候,发现自己线程ID和字段记录的一样,直接获锁。
b.当线程进入的时候,发现自己线程ID和字段记录的不一样,查看字段记录的线程ID的线程已经死了,那就将其重置为无锁,然后线程一起争抢。
c. 当线程进入的时候,发现自己线程ID和字段记录的不一样,查看字段记录的线程ID的线程依旧存活,那么立刻查找该线程的栈帧信息,如果发现其还需要持有锁,那么暂停当前线程,撤销偏向锁,升级为轻量级锁。******
d. 当线程进入的时候,发现自己线程ID和字段记录的不一样,查看字段记录的线程ID的线程依旧存活,那么立刻查找该线程的栈帧信息,如果线程不再使用该锁对象,那么将锁对象状态设为无锁状态,重新偏向新的线程。
2.轻量级锁
第一步:线程1获取轻量级锁时,会先把锁对象的对象头MarkWord复制一份到线程1栈帧中创建的用于存储锁记录的空间(称为DisplacedMarkWord),然后使用CAS把对象头中的内容替换为线程1存储的锁记录的地址;
第二步:如果在线程1复制对象头的同时(在线程1CAS之前),线程2也准备获取锁,复制了对象头到线程2的锁记录空间中,但是在线程2CAS的时候,发现线程1已经把对象头换了,线程2的CAS失败,那么线程2就尝试使用自旋锁来等待线程1释放锁。
第三步:但是如果自旋的时间太长也不行,因为自旋是要消耗CPU的,因此自旋的次数是有限制的,比如10次或者100次,如果自旋次数到了线程1还没有释放锁,或者线程1还在执行,线程2还在自旋等待,这时又有一个线程3过来竞争这个锁对象,那么这个时候轻量级锁就会膨胀为重量级锁。重量级锁把除了拥有锁的线程都阻塞,防止CPU空转。
3.重量级锁
在1.6之前,synchronized只存在重量级锁,作为一个JVM实现的锁,底层调用了Monitor Enter方法加锁,Monitor Exit方法释放锁,且实现了可重入。具体是每个锁对象会指向一个对应的monitor对象,通过monitor对象来记录重入次数以及占有锁的线程的信息。如果没有抢占到锁,底层还是调用park方法,来进行阻塞,且进入队列排队。
(2)volatile