偏向锁:
大多数情况下,不仅不存在多线程竞争,而且一个线程会一直去执行一个同步代码块。
基于这种情况,当环境中只有一个线程时,使用无锁机制:偏向锁。
当某个线程获取到对象的锁后,会将对象的对象头设置为:(锁标志位置为01,偏向锁标志为1,偏向线程ID为当前线程的ID),并在当前线程的栈帧中记录偏向锁信息。以后再执行同步代码块时,不用在执行CAS进行锁的获取与释放。
只需要测试对象的对象头中是否有当前线程的偏向锁(锁标志位置为01,偏向锁标志为1,偏向线程ID为当前线程的ID)。测试成功则执行同步代码块,失败的话,看对象的偏向锁标志位是否为1,如果为1则使用CAS去将对象的锁ID换成自己的,如果没有,去CAS获取获取偏向锁。
流程:
线程遇到同步代码块时,会先去看对象是否被加锁。如果加锁,判断是否为偏向锁,如果不是则执行轻量级锁流程,如果为偏向锁,判断对象的偏向线程ID是否为自己,如果是则执行同步代码块。如果不是,用CAS去将对象的偏向线程ID换为自己。换成功则获取到锁,设置对象头,置换失败则表明存在竞争,执行偏向撤销。
如果对象未被加锁,则先判断是否支持偏向锁(因为可通过JVM参数进行设定),如果没有进行轻量级锁流程。支持的话,会去CAS获取偏向锁。获取成功则把设置对象头并去执行代码块。获取失败则表明存在竞争,执行偏向撤销。
偏向撤销:
等到全局安全点(当前JVM未执行任何字节码)后,首先会暂停持有偏向锁定的线程,然后判断线程是否存活,如果未存活,则将对象头设置为无所状态。反之,将拥有偏向锁的栈帧执行完。然后判断是否将竞争线程设为偏向对象,或者将共享对象设为无锁状态。最后将之前拥有偏向锁的线程唤醒。
轻量级锁:
轻量级锁用CAS操作来代替使用重量级的阻塞现象。
流程:
线程1执行到带同步代码时,会CAS去将对象的MarkWord置换到自己栈帧的锁记录区,并将自己的锁记录区指向对象。然后去执行同步代码块。此时线程2来竞争,因为已经有锁,未能将MarkWord置换成功,然后线程就进行自旋等待。当线程1执行完毕后,再次进行CAS将MarkWord置换回去时失败,发现存在线程竞争就会发生锁膨胀。膨胀的同时线程2不再自旋进入阻塞,随后线程1后释放膨胀后的重量级锁并唤醒线程2.