乐观锁和悲观锁
这两种锁是面对并发情况下加锁的两种策略,并非是指特定的锁
乐观锁
取数据的时候不会对数据上锁,更新数据时候则会检查这个数据是否被别的线程修改,修改过则重新读取直到更新成功或者线程放弃操作
CAS策略(Compare-and-Swap),更新数据前先进行比较旧值是否发生了变化,如果是则更新为新数据,否则什么都不做重新循环
悲观锁
取数据的时候就会对数据上锁阻止别的线程取数据
sychronized锁流程
sychronized锁的升级过程是偏向锁->轻量级锁->重量级锁
重量级锁
重量级锁即是利用操作系统提供的同步机制实现加锁,JDK1.6之前只有这一种锁,对象头的锁标志位为10,mark word剩余部分是一个指向堆中monitor对象的指针。当一个线程申请对一个对象加锁时发现它加了重量级锁,会将线程挂起。
轻量级锁
当偏向锁升级为轻量级锁,没有强到锁的线程将自旋,通过CAS修改对象头里的锁标志位,如果释放了则将其设置为锁定,并且把持有者信息改为自己,如果为否,则接着比较,这样是为了用线程的自旋等待换取线程挂起的开销,如果忙等线程自旋次数超过默认的十次,就会将锁升级为重量级锁。
偏向锁
在实际情况中很可能出现加了锁但是很长时间只有一个线程对其进行操作,这样的话如果用轻量级锁或者重量级锁就会产生多次冗余操作(CAS操作),因此为了提高一个对象在一段很长的时间内都只被一个线程用做锁对象场景下的性能,使用sychronized时会先加偏向锁。
具体来说,对象创建时其mark word中的偏向锁为0,意思是匿名偏向,当一个线程加锁时,进行CAS操作将对象头的mark word中线程id改成当前id,之后该线程再对这个对象加锁,会发现该对象偏向这个线程,直接进行操作,几乎没有额外的性能开销。
如果另一个线程申请加锁,则会去查看偏向的线程是否还存活,如果存活且还在同步块中则将锁升级为轻量级锁,原偏向的线程继续拥有锁,当前线程则走入到锁升级的逻辑里;如果偏向的线程已经不存活或者不在同步块中,则将对象头的mark word
改为无锁状态(unlocked),之后再加偏向锁(这点存疑,也有可能JVM判定发生线程转化直接升级锁)。