synchronized 原理及JDK1.6后的优化_(3)

        在JDK1.5之前,若想实现线程同步,只能通过关键字synchronized这一种方式来实现。其底层Java也是通过synchronized关键字来实现的,做到数据的原子性维护的;synchronized关键字说JVM实现的一种内置锁,从底层角度来说,这种锁的获取与释放都是由JVM帮助实现的。

       从JDK1.5开始,并发包引入了Lock锁,Lock同步锁是基于Java实现的,因此锁的获取与释放都通过Java代码来实现与控制的;然而,synchronized是基于底层操作系统的mutexLock来实现的。每次对锁的获取与释放动作都会带来用户态和内核态之间的切换,这种切换极大地增加了系统的负担;在并发量较高时,也就是说锁的竞争比较激烈时,synchronized锁在性能上就表现的非常差。

-------------------------------------------

   从DK1.6开始,synchronized锁的实现发生了变化。JVM引入了相应的优化手段来提升synchronized锁的性能,这种提升,涉及到偏向锁,轻量级锁,重量级锁,从而减少锁的竞争所带来的用户态与内核态之间的切换。这种锁的优化实际上是通过Java对象头中的一些标志位实现的,对于锁的访问与改变,实际上都与Java对象头的信息息息相关。

  从DK1.6开始,对象实例在堆中会被划分为三个组成部分:

  1.   对象头:Markword;指向类的指针;数组长度 
  2. 实例数据;
  3. 对齐填充;

  其中,Markword记录了对象,锁及垃圾回收相关信息,在64位JVM中,其长度位64位,由如下组成:

  1. 无锁标记;
  2. 偏向锁标记;
  3. 轻量级锁标记;
  4. 重量级锁标记;
  5. GC标记。

   对于synchronized锁来说,锁的升级主要通过Markword中的锁标志位是否是偏向锁标志位达成的,synchronized锁都是先从偏向锁开始的。随着锁竞争的不断升级,逐步演化至轻量级锁,最后演变成了重量级锁。

锁的演化/升级历程:

  无锁-->偏向锁-->轻量级锁-->重量级锁

  偏向锁:

   针对一个线程来说的,它的主要作用就是优化同一个线程多次获取一个锁的情况;如果一个synchronized方法被一个线程访问,那么这个方法所在的对象就会在其Markword中将偏向锁进行标记,同时还有一个字段来存储该线程的ID;当这个线程再次访问同一个synchronized方法时,它会检查这个对象Markword的偏向锁标记以及是否指向了其线程id,如果是的话,那么线程就无需再进入管程(monitor)了,而是直接进入到改方法体中。如果是另外一个线程访问这个synchronized方法,偏向锁会被取消掉。

轻量级锁:

  若第一个线程已经获取当前对象的锁,这时第二个线程又开始尝试竞争抢该对象的锁,由于该对象的锁已经被第一个线程获取到,因此它是偏向锁,而第二个线程在争抢时,会发现该对象头中的Markword已经是偏向锁,但里面存储的线程ID并不是自己(是第一个线程),那么它会进行CAS,从而获取到锁,这里面存在两种情况:

  1.   获取锁成功、那么它会直接将Markword中的线程ID由第一个线程变成自己(偏向锁的标记位保持不变),这样该对象依然会保持偏向锁的状态。
  2. 获取失败:则表示可能会有多个线程在尝试争抢该对象的锁,那么偏向锁会进行升级,升级为轻量级锁。

   若自旋失败,并且无法获取到锁,那么锁就会转化为重量级锁,在这种情况下,无法获取到锁的线程都会进入到Monitor(即内核态)。

重量级锁:

  线程最终从用户态进入到内核态。

synchronized:

jdk8u/jdk8u/hotspot: 69087d08d473 src/share/vm/runtime/objectMonitor.hpp

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值