java锁synchronized底层实现,synchronized 偏向锁

  java锁synchronized底层实现,synchronized 偏向锁

  00-1010 1.偏置锁2的核心原理。撤销偏置锁3。偏置锁4的膨胀。偏置锁定的优势概述

  00-1010轻量级锁在没有竞争(只是自己的线程)的情况下,每次重新进入时仍然需要执行CAS操作。Java 6中引入了偏置锁进一步优化:只是第一次使用CAS将线程ID设置为对象的标记字头,然后就意味着没有竞争,不需要重新CAS。只要以后没有竞争,这个对象就归这个线程所有。

  public class Main { static final Object obj=new Object();public static void main(String[]args){ Thread Thread=new Thread(()-{ m1();});thread . start();}公共静态void m1(){ synchronized(obj){//sync block A m2();}}公共静态void m2(){ synchronized(obj){//同步块B m3();} } public static void m3(){ synchronized(obj){//biased state//synchronized block C } } } biased lock的核心原理是:如果一个没有线程竞争的线程获得了一个锁,那么这个锁就会进入一个偏置状态,标记字的结构就会变成偏置锁结构。锁对象的锁标志位(lock)改为01,biased_lock位改为1,然后线程的ID记录在锁对象的标志字中(由CAS操作完成)。以后线程获取锁时,可以通过判断线程ID和标志位直接进入同步块,甚至不需要CAS操作,省去了很多与锁申请相关的操作,从而提高了程序的性能。

  偏置锁的主要作用是消除无竞争的同步原语,进一步提高程序性能。因此,在没有锁竞争的情况下,偏置锁具有很好的优化效果。但是,一旦有第二个线程需要竞争锁,那么bias模式就会立即结束,进入轻量级锁的状态。

  如果大多数情况下同步块没有竞争,则可以通过偏置来提高性能。即在没有竞争的情况下,之前获取锁的线程在再次获取锁时,会判断偏向锁的线程ID是否指向自己。如果是这样,线程可以直接进入同步块,而无需再次获取锁。如果不指向当前线程,则当前线程会使用CAS操作将Mark Word中的线程ID设置为当前线程ID。如果CAS操作成功,将成功获取偏置锁,并执行同步代码块。如果CAS操作失败,则意味着存在竞争,抢锁线程将被挂起,占锁线程的偏置锁将被撤销,然后偏置锁将被扩展为轻量级锁。

  有偏锁过程如下:新线程只需要判断内置锁对象的Mark Word中的线程ID是否是自己的ID,如果是,就直接使用锁,不使用CAS交换;如果不是,比如第一次获取锁时内置锁的线程ID为空,则使用CAS交换,新线程将自己的线程ID交换到内置锁的标志字中。如果交换成功,则锁被成功锁定。

  在每一轮抢占过程中,JVM会将内置锁的偏向线程ID与当前线程ID进行比较。如果匹配,说明当前线程已经获得了偏置锁,当前线程可以快速进入临界区。因此,偏置锁定的效率非常高。简而言之,有偏锁是针对一个线程的,线程得到锁后,不会有解锁等操作,可以节省很多开销。

  偏向锁的缺点:如果锁对象经常被多个线程争用,那么偏向锁是多余的,其撤销的过程会带来一定的性能开销。

  00-1010如果有多个线程竞争有偏向的锁,这个对象锁已经有偏向了,其他线程发现有偏向的锁没有偏向自己,说明有竞争。尝试撤销偏锁(大概是引入安全点),然后扩展到轻量级锁。

  偏向锁撤销的开销花费还是挺大的,其大概过程如下:

  (1)在安全点停止拥有锁的线程。

  (2)遍历线程的堆栈帧,检查是否有锁记录。如果有锁记录,需要清除锁记录,使其解锁,修复锁记录指向的标志字,并清除其线程ID。

  (3)将当前锁升级为轻量级锁。

  (4)唤醒当前线程。

  因此,如果有两个或两个以上的线程在某些关键区域竞争,性能反而会因有偏向的锁定而降低。在这种情况下,可以在JVM启动时关闭偏置锁的默认功能。

  撤销偏向锁的条件:

  (1)多个线程竞争有偏向的锁。

  。

  (2) 调用偏向锁对象的hashcode()方法或者System.identityHashCode()方法计算对象的HashCode之后,将哈希码放置到Mark Word中,内置锁变成无锁状态,偏向锁将被撤销。

  

 

  

3. 偏向锁的膨胀

如果偏向锁被占据,一旦有第二个线程争抢这个对象,因为偏向锁不会主动释放,所以第二个线程可以看到内置锁偏向状态,这时表明在这个对象锁上已经存在竞争了。JVM检查原来持有该对象锁的占有线程是否依然存活,如果挂了,就可以将对象变为无锁状态,然后进行重新偏向,偏向为抢锁线程。

 

  如果JVM检查到原来的线程依然存活,就进一步检查占有线程的调用堆栈是否通过锁记录持有偏向锁。如果存在锁记录,就表明原来的线程还在使用偏向锁,发生锁竞争,撤销原来的偏向锁,将偏向锁膨胀(INFLATING)为轻量级锁。

  

 

  

4. 偏向锁的好处

经验表明,其实大部分情况下进入一个同步代码块的线程都是同一个线程。这也是JDK会引入偏向锁的原因。所以,总体来说,使用偏向锁带来的好处还是大于偏向锁撤销和膨胀所带来的代价。

 

  

 

  

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注盛行IT的更多内容!

 

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: