从JDK1.6开始,synchronized锁的实现发生了很大的变化;JVM引入了相应的优化手段来提升synchronized锁的性能,这种提升涉及到偏向锁,轻量级锁以及重量级锁,从而减少锁的竞争带来的用户态与内核态之间的切换;这种锁的优化实际上是通过java对象头中的一些标志位去实现的;对于锁的访问与改变,实际上都是与java对象头息息相关。
对象实例在堆中会被划分为三个部分:对象头,实例数据与对其填充。对象头也是由三块内容来构成:1.Mark Word 2.指向类的指针 3.数组长度
其中Mark Word(它记录了对象,锁及垃圾回收的相关信息,在64位的JVM中,其长度也是64bit的)的位信息包括如下组成部分:1.无锁标记 2.偏向锁标记 3.轻量级锁标记 4.重量级锁标记 5.GC标记
对于synchronized锁来说,锁的升级主要是通过Mark Word 中的锁标记位与是否是偏向锁标记为来达成的;synchronized关键字所对象的锁都是先从偏向锁开始,随着锁竞争的不断升级,逐步演化至轻量级锁,最后变成了重量级锁。
- 偏向锁:
针对一个线程来说的,主要作用是优化同一个线程多次获取一个锁的情况, 当一个线程执行了一个synchronized方法的时候,肯定能得到对象的monitor,这个方法所在的对象就会在Mark Work处设为偏向锁标记,还会有一个字段指向拥有锁的这个线程的线程ID。当这个线程再次访问同一个synchronized方法的时候,如果按照通常的方法,这个线程还是要尝试获取这个对象的monitor,再执行这个synchronized方法。但是由于Mark Word的存在,当第二个线程再次来访问的时候,就会检查这个对象的Mark Word的偏向锁标记,再判断一下这个字段记录的线程ID是不是跟第二个线程的ID是否相同的。如果相同,就无需再获取monitor了,直接进入方法体中。
如果是另一个线程访问这个synchronized方法,那么实际情况会如何呢?:偏向锁会被取消掉。
- 轻量级锁:
若第一个线程已经获取到了当前对象的锁,这是第二个线程又开始尝试争抢该对象的锁,由于该对象的锁已经被第一个线程获取到,因此它是偏向锁,而第二个线程再争抢时,会发现该对象头中的Mark Word已经是偏向锁,但里面储存的线程ID并不是自己(是第一个线程),那么她会进行CAS(Compare and Swap),从而获取到锁,这里面存在两种情况:
- 获取到锁成功(一共只有两个线程):那么它会将Mark Word 中的线程ID由第一个线程变成自己(偏向锁标记位保持不表),这样该对象依然会保持偏向锁的状态
- 获取锁师表(一共不止两个线程):则表示这时可能会有多个线程同时再尝试争抢该对象的锁,那么这是偏向锁就会进行升级,升级为轻量级锁
- 自旋锁,
若自旋失败,那么锁就会转化为重量级锁,在这种情况下,无法获取到锁的线程都会进入到moniter(即内核态),自旋最大的特点是避免了线程从用户态进入到内核态。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)