并发-深入分析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;)V
     flags: ACC_PUBLIC, ACC_STATIC
     Code:
         stack=2, locals=3, args_size=1
         0: getstatic #2 	// <- lock引用 (synchronized开始)
         3: dup
         4: astore_1 		// lock引用 -> slot 1
         5: monitorenter 	// 将 lock对象 MarkWord 置为 Monitor 指针
         6: getstatic #3 	// <- i
         9: iconst_1 		// 准备常数 1
         10: iadd 			// +1
         11: putstatic #3 	// -> i
         14: aload_1 		// <- lock引用
         15: monitorexit    // 将 lock对象 MarkWord 重置, 唤醒 EntryList
         16: goto 24
         19: astore_2 		// e -> slot 2 
         20: aload_1 		// <- lock引用										
         21: monitorexit 	// 将 lock对象 MarkWord 重置, 唤醒 EntryList
         22: aload_2 		// <- slot 2 (e)
         23: athrow 		// throw e
         24: return
 		Exception table:
             from to target type
             6 16 19 any
             19 22 19 any
         LineNumberTable:
         line 8: 0
         line 9: 6
         line 10: 14
         line 11: 24
         LocalVariableTable:
             Start Length Slot Name Signature
             0 25 0 args [Ljava/lang/String;
         StackMapTable: number_of_entries = 2
             frame_type = 255 /* full_frame */
         		offset_delta = 19
         		locals = [ class "[Ljava/lang/String;", class java/lang/Object ]
         		stack = [ class java/lang/Throwable ]
         frame_type = 250 /* chop */
        	 offset_delta = 4

synchronized原理进阶

轻量级锁

使用场景:如果一个对象虽然有多线程访问,但多线程访问的时间是错开的(也就是没有竞争),那么可以使用轻量级锁来优化

轻量级锁对使用者是透明的,语法仍是synchronized,会优先使用轻量级锁加锁,如果加锁失败才会用重量级锁

  • 创建锁记录(Lock Record)对象,每个线程都的栈帧都会包含一个锁记录的结构,内部可以存储锁定对象的 Mark Word在这里插入图片描述
  • 让锁记录中Object reference指向锁对象,并尝试用CAS替换 Object 的 Mark Word,将 Mark Word 的值存 入锁记录在这里插入图片描述
  • 如果CAS替换成功,对象头中存储了锁记录地址和状态 00,表示由该线程给对象加锁,这时图示如下

在这里插入图片描述

  • 如果CAS失败

    • 如果是其他线程已经持有了该Object的轻量级锁,这时表明有竞争,进入锁膨胀

    • 如果是自己执行了synchronized锁重入,那么再添加一条Lock Record作为重入的计数
      在这里插入图片描述

  • 当退出synchronized代码块(解锁时)如果有取值为null的锁记录,表示有重入,这时重置锁记录,表示重入计数减一

  • 当退出synchronized代码块时,锁记录的值不为null,这时使用CAS将MarkWord的值恢复给对象头

    • 成功,代表解锁成功
    • 失败,说明轻量级锁进行锁膨胀或已经升级为重量级锁,进入重量级锁解锁流程
锁膨胀

如果尝试加轻量级锁的过程中,CAS操作无法成功,这时一种情况就是有其他线程为此对象加上了轻量级锁(有竞争),这时需要进行锁膨胀,将轻量级锁变为重量级锁

  • 当Thread-1进行轻量级加锁时,Thread-0已经对该对象加了轻量级锁

    在这里插入图片描述

  • 这时Thread-1加轻量级锁失败,进入锁膨胀

    • 即为Object对象申请Monitor锁,让Object指向重量级锁地址
    • 然后自己进入Monitor的EntryList阻塞 BLOCKED在这里插入图片描述
  • 当Thread-0退出同步代码块解锁时,使用CAS将MarkWord的值恢复给对象头,失败。这时会进入重量级锁解锁流程,即按照Monitor地址找到monitor地址找到Monitor对象,将Owner设置为null,唤醒EntryList中的BLOCKED线程

自旋优化

重量级锁竞争的时候,可以通过自旋重试来进行优化,可以一定程度上避免阻塞
在这里插入图片描述

  • 自旋会占用CPU时间,单核CPU自旋就是浪费,多核CPU自旋才能发挥优势
  • 在java6之后自旋锁是自适应的,比较智能
  • java7之后不能控制是否开启自旋
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

涛堆堆

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值