文章插图
我们来做简单解释:
- 每个obj都由MarkWord来绑定一个Monitor
- 每个线程都需要经过synchronized(obj)方法进入Monitor
- 首先Monitor主要分为三个部分:WaitSet,EntryList,Owner
- Owner:属于当前Monitor的正常运行区间,例如Thread-2就是目前运行线程
- EntryList:属于当前Monitor的等待运行区间 , 需要等到Thread-2结束线程释放锁资源,Thread3等才可以抢夺锁
- WaitSet:属于之前获得过锁,但条件不满足进入 WAITING 状态的线程 , 后面讲 wait-notify 时会分析
- synchronized 必须是进入同一个对象的 monitor 才有上述的效果
- 不加 synchronized 的对象不会关联监视器,不遵从以上规则
synchronized原理我们会从底层代码来讲解synchronized的原理:
/*源码*/static final Object lock = new Object();static int counter = 0;public static void main(String[] args) {synchronized (lock) {counter++;}}/*底层字节码*/public static void main(java.lang.String[]);descriptor: ([Ljava/lang/String;)Vflags: ACC_PUBLIC, ACC_STATICCode:stack=2, locals=3, args_size=1// 下面是正式字节码过程// 正常运行阶段0: getstatic #2 // <- lock引用 (synchronized开始)3: dup4: astore_1 // lock引用 -> slot 1(这里提前存储一份锁对象,用于后续的解锁)5: monitorenter // 将 lock对象 MarkWord 置为 Monitor 指针(这里进行了上锁)6: getstatic #3 // <- i9: iconst_1 // 准备常数 110: iadd // +111: putstatic #3 // -> i14: aload_1 // <- lock引用15: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList(这里进行了解锁)16: goto 24// 报错补救阶段(去除锁)19: astore_2 // e -> slot 220: aload_1 // <- lock引用21: monitorexit // 将 lock对象 MarkWord 重置, 唤醒 EntryList22: aload_2 // <- slot 2 (e)23: athrow // throw e// 代码结束24: return// 这里是异常检测器:当出现异常时,移动到异常处理底层代码区域进行解锁操作Exception table:from to target type61619any192219anyLineNumberTable:line 8: 0line 9: 6line 10: 14line 11: 24LocalVariableTable:Start Length Slot Name Signature0250args [Ljava/lang/String;StackMapTable: number_of_entries = 2frame_type = 255 /* full_frame */offset_delta = 19locals = [ class "[Ljava/lang/String;", class java/lang/Object ]stack = [ class java/lang/Throwable ]frame_type = 250 /* chop */offset_delta = 4
轻量级锁我们首先来简单介绍一下轻量级锁:- 如果一个对象虽然有多线程要加锁,但加锁的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化 。
// 下面两个代码都调用了锁,但是他们归根结底属于一个流程,时间错开,这时系统就会避免直接使用Monitor而是用轻量级锁进行优化static final Object obj = new Object();public static void method1() {synchronized( obj ) {// 同步块 Amethod2();}}public static void method2() {synchronized( obj ) {// 同步块 B}}
我们来展示一下实现流程(00轻量级锁,01无锁):- 创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的Mark Word
文章插图
- 让锁记录中 Object reference 指向锁对象,并尝试用 cas 替换 Object 的 Mark Word,将 Mark Word 的值存入锁记录
文章插图
- 如果 cas 替换成功,对象头中存储了 锁记录地址和状态 00 ,表示由该线程给对象加锁,这时图示如下
文章插图
- 当退出 synchronized 代码块(解锁时)如果有取值为 null 的锁记录,表示有重入,这时重置锁记录 , 表示重入计数减一
文章插图
- 当退出 synchronized 代码块(解锁时)锁记录的值不为 null,这时使用 cas 将 Mark Word 的值恢复给对象头
- 如果是其它线程已经持有了该 Object 的轻量级锁,这时表明有竞争,进入锁膨胀过程
推荐阅读
- Seata Server 1.5.2 源码学习
- 2022极端高温!机器学习如何预测森林火灾?? 万物AI
- 1.nginx学习
- 常用Python库整理
- 图学习参考资料 词向量word2vec
- 五 RK3568开发笔记:在虚拟机上使用SDK编译制作uboot、kernel和ubuntu镜像
- realme笔记本最新消息_realme笔记本即将发布
- JUC学习笔记——进程与线程
- Seata 1.5.2 源码学习
- 第4版 高性能MySQL 第一章 MySQL架构 读书笔记