JDK1.6后,内建锁被优化为三种锁:
--无锁状态:最初没有任何线程尝试获取锁。
偏向锁:认为从始至终只有一个线程在竞争同一把锁。(最乐观)只在第一次请求锁时采用CAS操作。
轻量级锁:多个线程在不同时间段请求同一把锁,(基本不存在锁竞争)。每次进入同步块时都会进行CAS操作。可以避免线程的阻塞及唤醒。
重量级锁:悲观锁,即为JDK1.5以前的内建锁,会阻塞,唤醒请求加锁的线程,针对多线程在同一时刻竞争同一把锁,当线程获取到对象锁后,将其他参与竞争的线程阻塞。JVM会采用自适应自旋避免在非常小的同步块时阻塞线程。
1.无锁状态:对象头中的锁标志位为无锁,即:
是否为偏向锁:0
锁标志位:01
2.偏向锁:对象头中锁标志位为偏向锁,即:
是否为偏向锁:1
锁标志位:01
//1,2中,是否为偏向锁就是无锁和偏向锁的区别
3.轻量级锁:00
4.重量级锁: 10
5.GC标记:11
内建锁的升级过程:
无锁状态升级为偏向锁及偏向锁的运行:
最初对象锁是无锁状态,当有一个线程希望拿到对象锁时,首先会检查对象头中的Mark Word,其中存储的线程ID是否与自己的线程ID相同,因为刚开始,Mark Word存储的线程ID一定为null,与当前线程ID一定不同,所以会继续判断Mark Word:是否为偏向锁,此时偏向锁标志为0,说明当前为无锁状态,此时会将当前线程的ID写入Mark Word中,并将是否为偏向锁标志置为1,线程进入代码块中运行。若偏向锁标志位为1,则不断尝试CAS操作争夺锁,或者在全局安全点时将偏向锁撤销,升级为轻量级锁。
在尝试CAS竞争锁时,新线程首先会暂停原来线程的运行,将Mark Word中线程ID置空,然后新老线程对对象锁进行争夺,当Mark Word中线程ID被置空次数超过一定限度后,JVM会认为偏向锁已经不适合当前对象锁,此时,偏向锁升级为轻量级锁。
(图中最下方方框中“获”字,打错了,应该是“或”。//手动笑哭脸)
偏向锁运行流程及竞争后膨胀:
重量级锁的运行及竞争:
重量级锁会阻塞竞争锁失败的线程,并在在锁释放时唤醒这些被阻塞的线程。
自适应自旋:
根据上一次自旋是否得到锁决定下一次自旋时间的长短,当第一次成功获得到锁,JVM希望在第二次还继续可以得到锁,就会增加自旋的时间,提升再次得到锁的概率;而当第一次竞争没有得到锁,JVM第二次也不会报太大希望,就会减少下次自旋时间。