synchronized-锁的优化

较早的jdk版本中,synchronized属于重量级锁(Monitor实现),在线程争用不到锁的时候将会线程阻塞,并且下次争用锁的时候还需要从阻塞状态醒来,这就是两次线程状态的切换,如果保护的代码块的实际执行耗时还没有线程切换耗时长,就得不偿失了。

基于这种情况,在jdk1.6中,对sycnhronized做了很大优化,增加了两种锁的状态:

  • 偏向锁(Biased Locking)
  • 轻量级锁(Lightweight Locking)

到此,锁的状态增加为无锁、偏向锁、轻量级锁、重量级锁4种。可以通过对象头的锁标识位来确定当前锁的状态。
synchronized用到的锁,是存储在对象头中的(这也是Java所有对象都可以上锁的根本原因)。HotSpot虚拟机中,对象头包括两部分信息:Mark Word(对象头)Klass Pointer(类型指针)

  • Klass Pointer :是指向对象类型的指针,用来确定该对象是什么类型。
  • Mark Word:又分为两部分。第一部分根据当前锁状态的不同,储存的数据也不同,可以是对象自身的运行时数据,例如哈希码,GC分代年龄,线程持有的锁,偏向时间戳等。这一部分的长度是不固定的。第二部分是末尾两位,存储锁标志位,表示当前锁的级别。
    Mark Word.png
  • 偏向锁
    可以在没有发生锁竞争的状态下,减少轻量级锁的执行次数,降低cpu的消耗。
    根据研究表明,在大部分的时间里,锁并不存在被争抢的情况,并且锁总是被同一个线程反复获取,所以为了减少获取轻量级锁(CAS自旋)带来的消耗,当一个线程拿到锁时,先进入偏向锁状态,当这个线程再次获取锁时,直接拿到锁,无需做更多的保证同步的操作,减小损耗,提高性能。

  • 轻量级锁
    又称自旋锁。在发生锁竞争的状态下,用不断自旋获取锁的方式来代替每次都需要进行线程切换的重量级锁,避免了线程切换带来的不必要的开销。
    但是自旋锁本身又有它自己的弊端,如果锁的竞争比较激烈,长时间获取不到锁资源,那么不断自旋带来的性能开销有可能比线程切换还要大,所以jdk1.6后引入了适应式自旋锁(根据运行时的实际情况,不断调整允许自旋的最大次数,以此来达到最低开销)。

你可能感兴趣的:(synchronized-锁的优化)