-
初认synchronized使用规则
-
一般来说,使用规则一般分为两类,即修饰代码块和修饰方法
-
修饰代码块:
synchronized(this|object) { // TODO } synchronized(类.class) { //TODO }
-
修饰方法:
- 修饰非静态方法
- 修饰静态方法
-
-
从使用规则可以看出,synchronized(this|object),括号内绑定的是对象,那么synchronized的实现原理应该和对象有关系的。
- 那么,在JVM中是如何存储一个对象的,了解一下对象内存布局。
- 一个对象内存布局分为:对象头,实例数据,数据填充。
- 其他的先不管,直接看向对象头,JVM解释如下:
- 对象头分为两个部分:其一(对象标记),,即Hash码、GC分代年龄、锁状态标记等。其二(元数据),即包含类型指针,即对象指向它元数据的指针,JVM通过该指针确定这个对象是哪个类的实例。
- 对象头中保存有锁标记的部分,称为对象标记,也称为Mark Word。
- 那么,在JVM中是如何存储一个对象的,了解一下对象内存布局。
-
初识Mark Word概念
-
由上文可,mark word就是对象标记,在JVM底层实现如下:
class oopDesc { friend class VMStructs; private: volatile markOop _mark; //对象标记 union _metadata { Klass* _klass; narrowKlass _compressed_klass; } _metedate }
-
打开markOop类,部分结构如下:
- 枚举类,字面意思上可以理解,包含分代年龄、锁标记、偏向锁标记、哈希码等。
- 后面的等号赋值,表示在内存中占用几个比特bit。
-
-
从markword可以看出,synchronized实现跟对象有关,即对象锁(对象监视器)。
-
那么在markOop类中的枚举,我们需要关注几个字段:lock_bits(锁)、biased_lock_bits(偏向锁)
- 在JVM中锁分类:无锁、偏向锁、轻量级锁、重量级锁
- biased_lock_bits两个比特:0,表示未持有偏向锁。1,表示持有偏向锁。
- lock_bits一个比特:00(无锁/偏向锁)、01(轻量级锁)、10(重量级锁)、11(GC标记)
- 当biased_lock_bits为1时,lock_bits为00的时候,表示偏向锁。
-
其实,在JDK1.6之前,synchronized的实现都是重量级锁,在1.6之后进行了优化,这些锁优化概念,包括偏向锁,轻量级锁,自旋,锁膨胀等,synchronized底层原理与这些知识点分不开。
-
那么什么叫偏向锁?
- 偏向锁是JDK1.6之后引入的一种锁优化,目的就是消除数据在无竞争情况下的同步原语。如果说轻量锁是在无竞争环境情况下使用CAS操作去消除同步使用的互斥量,那么偏向锁就是在无竞争环境情况下把整个同步都消除掉,连CAS操作都不要做。
- 其中,偏向锁含义就是这个锁会偏向第一个获得它的线程,如果在接下来的执行过程中,该锁没有被其他线程获取,那么持有该偏向锁的线程将永远不需要进行同步。比如,线程A买了一张游乐园的票,在游客数目较小且不产生竞争的情况下,线程A可以自由进出游乐园,而不必去竞争一张门票。
-
偏向锁是如何获取的?
-
获取目标对象的mark word,synchronized(object){},即获取object对象的对象头信息。
-
假设JVM开启偏向锁模式,即启用参数-XX:+UseBiasedLocking,那么对象(object)第一次被线程获取的时候,JVM会把其对象头中的标志biased_lock_bits为1时,lock_bits为00,表示开启偏向模式,同时使用CAS操作把获取到这个锁的线程ID记录到对象头(mark word)中,如果CAS操作成功,持有偏向锁的线程有每次进入这个锁相关的同步块时,JVM都不会进行任何同步操作。
-
然而,当有另外一个线程去尝试获取这个锁时,产生竞争,所有偏性模式结束。之后,根据锁对象当前是都处于被锁定状态进行撤销偏向。
-
-
什么叫轻量级锁?
- 如果完全没有锁竞争,那么申请重量级锁是无意义的。那么轻量级锁的目标是,减少无实际竞争情况下,使用重量级锁产生的性能消耗,包括系统的调用引起的内核态与用户态切换、线程阻塞造成的线程间切换等。
-
轻量级锁获取和释放
- 轻量级锁的获取:
- JVM首先会在当前线程的栈帧中创建一个帧记录,称为锁记录空间(Lock Record),用于存储锁对象目前的Mark word的拷贝(Displaced Mark Word),然后,JVM会尝试使用CAS操作将对象的Mark Word更新为指向Lock Record的指针。
- 如果成功,则当前线程就拥有对象锁,并且对象的Mark Word中的标记lock_bits = 01,表示此对象处于轻量级锁定状态。
- 如果失败,JVM会首先检查对象的Mark word是否指向当前线程的栈帧。如果指向,说明当前线程已经拥有了这个对象的锁,那么就可以直接进入同步代码块。如果没有指向,则说明这个锁对象已经被其他线程抢占。
- 如果两条线程以上在争抢同一个锁,那轻量级锁不在有效,锁进行膨胀,膨胀成重量级锁。
- 轻量级锁的获取:
java多线程基础之Synchronized底层实现
最新推荐文章于 2024-06-07 17:00:00 发布