2个线程争取偏向锁,就会膨胀为轻量级锁
2个线程争取轻量级锁,就会膨胀为重量级锁,那不是说这两个锁都是只有单线程下才存在的吗?
有啥区别吗,为什么不直接从偏向锁直接膨胀到重量级锁
对于同一个线程多次获得锁,使用偏向锁,也就是说没有发生线程争抢,一个线程在访问加了synchronized修饰的代码块时,会在对象头中存储当前线程的id,之后该线程进入和退出时,不需要再次加锁和释放锁,而是直接比较对象头里是否存储了指向当前线程ID的偏向锁
多个线程在不同时段获取同一把锁,就会变成轻量级锁,当锁是偏向锁的时候,被另外的线程所访问,偏向锁就会升级为轻量级锁
偏向锁: 是减少CAS原子指令,减少cpu的执行开销
重量锁: 线程会释放cpu 资源 进入阻赛
平凡切换线程对CPU的开销,比较大
偏向锁
偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。
在大多数情况下,锁总是由同一线程多次获得,不存在多线程竞争,所有出现了偏向锁。其目的就是在只有一个线程执行同步代码块时能够提高性能。
当一个线程访问同步代码块并获取锁时,会在mark word里存储偏向线程id。在线程进入和退出同步块时不在通过CAS操作来加锁和解锁。偏向锁时为里在无锁线程竞争的情况下尽量减少不必要的轻量级锁执行路径,因为轻量锁获取及释放锁依赖CAS原子指令,而偏向锁只需要在置换ThreadID的时候依赖一次CAS原子指令即可。
偏向锁只有遇到其他线程竞争偏向锁时,持有偏向锁的线程才会释放锁,线程不会主动释放偏向锁。偏向锁的撤销,需要等待全局安全点(这个时间点上没有字节码正在执行)先暂停拥有偏向锁的线程,判断对象是否处于锁定状态。撤销偏向锁后恢复到无锁(标志01)或轻量级锁(00)状态
轻量级锁
当锁时偏向送锁的时候,被另外的线程锁访问,偏向锁会升级为轻量锁,其他线程会通过自旋的形式获取锁,不会阻塞,从而提高性能。
在代码进入同步代码块的时候,如果同步对象锁的状态为无锁状态(锁标志位 01 ,是否为偏向锁 0) ,虚拟机首先将当前线程的展帧中建立一个名为锁记录(lock Record)空间,用于存储锁的对象目前的Mark Word的拷贝,然后拷贝对象头中Mark Word复制到锁记录中。
拷贝成功后,虚拟机将使用CAS操作尝试将对象的Mark Word更新为指向Lock Record的指针。
如果这个操作成功,那么这个线程就用于两该对象的锁,并且对象Mark Word的锁标志位设置为“00”,表示对象处于轻量级锁状态。
若当前只有一个等待线程,则该线程痛殴自旋进行等待。但时当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访问,轻量锁升级为重量锁。
重量级锁
锁的标志位改为 01 线程进入阻塞状态。
偏向所锁,轻量级锁都是乐观锁,重量级锁是悲观锁。
一个对象刚开始实例化的时候,没有任何线程来访问它的时候。它是可偏向的,意味着,它现在认为只可能有一个线程来访问它,所以当第一个线程来访问它的时候,它会偏向这个线程,此时,对象持有偏向锁。偏向第一个线程,这个线程在修改对象头成为偏向锁的时候使用CAS操作,并将对象头中的ThreadID改成自己的ID,之后再次访问这个对象时,只需要对比ID,不需要再使用CAS在进行操作。一旦有第二个线程访问这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到对象时偏向状态,这时表明在这个对象上已经存在竞争了,检查原来持有该对象锁的线程是否依然存活,如果挂了,则可以将对象变为无锁状态,然后重新偏向新的线程,如果原来的线程依然存活,则马上执行那个线程的操作栈,检查该对象的使用情况,如果仍然需要持有偏向锁,则偏向锁升级为轻量级锁,(偏向锁就是这个时候升级为轻量级锁的)。如果不存在使用了,则可以将对象回复成无锁状态,然后重新偏向。
轻量级锁认为竞争存在,但是竞争的程度很轻,一般两个线程对于同一个锁的操作都会错开,或者说稍微等待一下(自旋),另一个线程就会释放锁。 但是当自旋超过一定的次数,或者一个线程在持有锁,一个在自旋,又有第三个来访时,轻量级锁膨胀为重量级锁,重量级锁使除了拥有锁的线程以外的线程都阻塞,防止CPU空转。