公平锁 轻量锁

非公平锁的获取流程,允许插队,直接尝试CAS操作,尝试失败时才执行入队。竞争激烈的环境中,非公平锁直接尝试锁定操作可以提高系统吞吐量。如果当前线程请求锁时,正好锁刚刚被释放,其他等待线程还没有被唤醒,在这过程中该线程完成了相关操作并释放锁,此时其他线程刚好被唤醒。利用线程唤醒的延时,其他线程能够完成操作,且不影响该阻塞线程唤醒后获取锁。


     非公平锁在请求获取锁时,直接尝试CAS操作获取锁,如果尝试失败才进行排队。在竞争激烈的情况下,非公平锁的性能高于公平锁的一个原因是:活动线程直接尝试获取锁的操作时间可能要比恢复一个阻塞线,并把锁分配给它的时间短的多(线程的唤醒操作需要额外的时间,从唤起到线程真正运行之间存在着严重的时延)。


轻量级锁也是一种多线程优化,它与偏向锁的区别在于,轻量级锁是通过CAS来避免进入开销较大的互斥操作,而偏向锁是在无竞争场景下完全消除同步,连CAS也不执行(CAS本身仍旧是一种操作系统同步原语,始终要在JVM与OS之间来回,有一定的开销)。

轻量级锁(LightweightLocking)本意是为了减少多线程进入互斥的几率,并不是要替代互斥。

它利用了CPU原语Compare-And-Swap(CAS,汇编指令CMPXCHG),尝试在进入互斥前,进行补救

轻量级锁加锁:线程在执行同步块之前,JVM会先在当前线程的栈桢中创建用于存储锁记录的空间,并将对象头中的MarkWord复制到锁记录中,官方称为DisplacedMark Word。然后线程尝试使用CAS将对象头中的MarkWord替换为指向锁记录的指针。如果成功,当前线程获得锁,如果失败,表示其他线程竞争锁,当前线程便尝试使用自旋获取锁

轻量级锁解锁:轻量级解锁时,会使用原子的CAS操作来将DisplacedMark Word替换回到对象头,如果成功,则表示没有竞争发生。如果失败,表示当前锁存在竞争,锁就会膨胀成重量级锁。下图是两个线程同时争夺锁,导致锁膨胀的流程图。


讲了synchronized锁的具体实现,就将偏向锁,自旋锁,轻量级锁,重量级锁 这些概念串起来了。偏向锁,在上篇博客中已经有所讲到,因此,这篇博客重点讲下偏向锁获取失败后的步骤。


偏向锁获取失败后,synchronized锁进入竞争轻量级锁的阶段。具体步骤如下:

java Object Model中定义,Object Header是一个2字(1 word = 4 byte)长度的存储区域。
第一个字长度的区域用来标记同步,GC以及hash code等,官方称之为 mark word。第二个字长度的区域是指向到对象的Class。

在2个word中,mark word是轻量级锁实现的关键。它的结构见下表

synchronized锁的具体实现 - silver9886@126 - silver9886@126的博客

从表中可以看到,state为lightweight locked的那行即为轻量级锁标记。bitfieds名为指向lock record的指针,这里的lock record,其实是一块分配在线程堆栈上的空间区域
用于CAS前,拷贝object上的mark word(为什么要拷贝,请看下文)。
第三项是重量级锁标记。后面的状态单词很有趣,inflated,译为膨胀,在这里意思其实是锁已升级到OS-level

具体流程如下:

synchronized锁的具体实现 - silver9886@126 - silver9886@126的博客

 这里需要解释的是:

lock record:是一块分配在线程堆栈上的空间区域,因此每个线程都有自己的一个lock record。

object mark word 里的轻量级锁指针指向是会指向真正的锁的拥有者的lock record所在的stack指针,获取锁的线程的lock record的owner字段会指向object mark word,作用是为了在接下里的运行过程中,识别哪个对象被锁住了。

synchronized锁的具体实现 - silver9886@126 - silver9886@126的博客
注意此时是没有进程和当前进程进行竞争锁的时候,采用的是轻量级锁。此时 object mark word里tag bits是00
如果此时有第二个进程尝试获取锁(即已经存在锁的竞争), 则轻量级锁将升级为重量级锁! object mark word里tag bits是10, object mark word里的存储的是指向重量级锁(互斥量)的指针,请求的进程进入到阻塞状态。 (关键点在于线程1轻量锁之后,线程2试图获取锁的操作,将mark word中存在的锁状态已经改变为10,此时,在load record中的锁信息和mark word中的锁信息不一致了(load record中锁状态是01,而mark word中锁状态是10),在进入上图中第二个cas操作时,将失败,之后会释放锁,同时唤醒阻塞线程)
以上就是轻量级锁和重量级锁的转换状态变化。synchronized锁一个不同是,在获取轻量级锁失败的时候,并不会立即进入阻塞状态,而是不停的重试,重新试图获取轻量级锁。这种行为,我们称之为自旋(就是cpu不停,不断尝试获取锁)。当自旋失败后,synchronized进程将进入阻塞状态,也就是锁变为了重量级锁
总结:
synchronized线程,获取锁的过程为:偏向锁-》轻量级锁-》自旋锁-》重量级锁
java设计的时候,那省掉轻量级锁的过程,当 偏向 锁获取失败的时候,直接进入自旋锁,或是修改锁的状态,升级到重量锁,行不行?
因为 偏向 锁解除的前提一定也是锁产生了竞争,最终锁的状态一定是从轻量级锁升级为重量级锁,最终重量级锁指向互斥变量,另一个进程的load record中锁信息是轻量级锁。这不是多余的操作么?省略掉 偏向 锁到轻量级锁的过程,没有轻量级锁,不是更好?
这个问题没人回答,我猜测是因为,偏向锁是java6才加入了,为了向前保持兼容,所以还是保留了轻量级锁。

参考了 
深入理解java虚拟机
轻量级锁 http://blog.csdn.net/songylwq/article/details/5585734


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值