https://www.cnblogs.com/gustavo

Gustavo's Blog

人类的赞歌是勇气的赞歌!

[Thread] Synchronized

1.Monitor对象

Monitor对象被称为管程或者监视器锁
在Java中,每一个对象实例都会关联一个Monitor对象。
这个Monitor对象既可以与对象一起创建销毁,也可以在线程试图获取对象锁时自动生成。
当这个Monitor对象被线程持有后,它便处于锁定状态。Synchronized的基本原理就是能否获取Monitor的所有权。

monitor
  ObjectMonitor() {
    _header       = NULL;
    _count        = 0; //用于记录线程获取锁的次数,成功获取到锁后count会加1,释放锁时count减1
    _waiters      = 0,
    _recursions   = 0;// 线程重入次数
    _object       = NULL;  // 存储Monitor对象
    _owner        = NULL;  // 持有当前线程的owner
    _WaitSet      = NULL;// wait状态的线程列表
    _WaitSetLock  = 0 ;
    _Responsible  = NULL ;
    _succ         = NULL ;
    _cxq          = NULL ;  // 单向列表
    FreeNext      = NULL ;
    _EntryList    = NULL ;// 处于等待锁状态block状态的线程列表
    _SpinFreq     = 0 ;
    _SpinClock    = 0 ;
    OwnerIsThread = 0 ;
    _previous_owner_tid = 0;
  }

放 作 权  井 离 开 同 步  EntrySet  待 进 入 集 合  线 程  线 程  Owner  对 象 操 作 权 持 有 者  建 等 方 法  放 操 作 权  Obj 巳 1  M onitor  Obj 巳 2  M onitor  但 还 ,  线 程  开 同  线 程  线 程  WaitSet  待 授 权 集 合  线 程  线 程  线 程  Notify/N  线 程  EntrySet  待 进 入 集 合  线 程  同 步 块  得 对 象  Owner  对 象 操 作 权 持 有 者  线 程  获 得 对 象 操 作 权  WaitSet  待 授 权 集 合

2.对象头

在对象头的MarkWord中主要存储了对象自身的运行时数据,例如哈希码、GC分代年龄、锁状态、线程持有的锁、偏向线程ID等

25bit  Ibit  2bit  锁 状 态  4bit  是 否 偏 向 锁  锁 标 志 位  23bit  2bit  对 象 的 hashcode  分 代 年 龄  无 锁 状 态  0  0 0 0 1 1  内 上 内 上 0 0 内 上  偏 向 锁  分 代 年 龄  线 程 id  Epoch  1  指 向 栈 中 锁 记 录 ( L 。 Ck Record) 的 指 针  轻 量 级 锁  指 向 重 量 级 锁 ( M 。 nit 。 r 对 象 ) 的 指 针  重 量 级 锁  GC 标 记
当对象为偏向锁时,Mark Word存储了偏向线程的ID;
当状态为轻量级锁时,Mark Word存储了指向线程栈中Lock Record的指针;
当状态为重量级锁时,Mark Word存储了指向堆中的Monitor对象的指针。

3.锁升级

偏向锁

偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word的结构也变为偏向锁结构,即将对象头中Mark Word的标记位改为1,并且在Mark Word中记录该线程的ID。

轻量级锁

Mark Word中的部分字节CAS更新指向线程栈中的LockRecord,如果更新成功,则轻量级锁获取成功_,记录锁状态为轻量级锁;否则,说明已经有线程获得了轻量级锁,目前发生了锁竞争(不适合继续使用轻量级锁),接下来膨胀为重量级锁。
JVM会在当前线程的线程栈中开辟一块单独的空间,里面保存指向对象锁MarkWord的指针,同时在对象锁Mark Word中保存指向这片空间的指针。上述两个保存操作都是CAS操作,如果保存成功,代表线程抢到了同步锁,就把MarkWord中的锁标志位改成00,可以执行同步代码。如果保存失败,表示抢锁失败

自旋锁

空循环一般不会执行太多次,可能是50个循环或100循环,在经过若干次循环后,如果得到锁,就顺利进入同步代码。如果还不能获得锁,那就会将线程在操作系统层面挂起,即进入到重量级锁。

重量级锁

重量级锁通过对象内部的monitor实现(见上文的MonitorObject模式)
monitor的本质是依赖于底层操作系统的MutexLock实现,操作系统实现线程间的切换是通过用户态与内核态的切换完成的,而切换成本很高
MutexLock最核心的理念就是尝试获取锁.若可得到就占有.若不能,就进入睡眠等待

4.其他概念

锁消除

JVM在编译时通过对运行上下文的描述,去除不可能存在共享资源竞争的锁,通过这种方式消除无用锁,即删除不必要的加锁操作,从而节省开销

锁膨胀

将多次连接在一起的加锁、解锁操作合并为一次,将多个连续的锁扩展成一个范围更大的锁
如果虚拟机检测到有一系列连串的对同一个对象加锁和解锁操作,就会将其合并成一次范围更大的加锁和解锁操作,即在第一次append方法时进行加锁,最后一次append方法结束后进行解锁

posted @ 2022-04-02 15:43  BitBean  阅读(342)  评论(0编辑  收藏  举报