帮你深入了解synchronized关键字

学习这些锁之前先来了解一下MarkWord。

整个对象头在32位操作系统下的内存分配
由此可见java的设计者们,真的是把mark-word设计到了极限。都是为了省内存啊。
内容都在图里。就不多赘述了。

什么是重量锁?

这个我觉得人人都应该清楚,其实java以前如果要解决线程安全问题。就要依赖于操作系统帮忙生成monitor监视器对象。你就认为是“锁”,由监视器对象来协调线程安全问题。
之所以叫 重量锁 就是因为如果使用这种方式,就要依赖于操作系统帮忙。会涉及到内核态和用户态的转换。(这块如果不理解可以百度哦)
java代码:

public class SyncDemo {
    
    private  Integer i = 0;
    
    public void test(){
        synchronized (this){
            i++;
        }
    }
}

编译后的字节码:

  public void test();
    Code:
       0: aload_0
       1: dup
       2: astore_1
       3: monitorenter					// 这里向操作系统申请锁
       4: aload_0
       5: getfield      #3                  // Field i:Ljava/lang/Integer;
       8: astore_2
       9: aload_0
      10: aload_0
      11: getfield      #3                  // Field i:Ljava/lang/Integer;
      14: invokevirtual #4                  // Method java/lang/Integer.intValue:()I
      17: iconst_1
      18: iadd
      19: invokestatic  #2                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
      22: dup_x1
      23: putfield      #3                  // Field i:Ljava/lang/Integer;
      26: astore_3
      27: aload_2
      28: pop
      29: aload_1
      30: monitorexit			// 释放锁
      31: goto          41
      34: astore        4
      36: aload_1
      37: monitorexit		// 异常失败也释放锁
      38: aload         4
      40: athrow
      41: return
    Exception table:
       from    to  target type
           4    31    34   any
          34    38    34   any

其实重量锁,就是new 了一个monitor对象,

  1. 加锁时把对象(syn关键字锁的对象)的mark-word copy到monitor对象中,并将30bit保存为指向monitor的ref引用指针
  2. 解锁时复原回mark-word

什么是轻量锁?

上面说到,重量锁性能比较差,毕竟代码是要时时刻刻执行的。那么这群java设计者就想,我能不能在java用户态层面就把锁这个东西给控制住了。不需要使用那么重的监视器。
所以他们设计出了轻量锁,可以类比上面加锁解锁过程。

只不过加锁解锁通过CAS 操作mark-word对象头。因为CAS操作是非常小并发代价。

CAS过程

  • 加锁线程将对象(syn关键字锁的对象)的mark-word copy到本线程的栈帧中。
  • 将原mark-word中的30bit 内容保存成指向栈帧的ref引用地址
  • 解锁时同样,只要将栈帧中的copy-mark-word复原会对象头。

CAS 操作会有成功和失败。成功即表示加锁成功,失败即表示加锁失败。此时升级重量锁。
思考:

  1. 什么时候会失败?
  2. 如果一个线程已经加过轻量锁,但是被另一个线程申请成了重量锁。它CAS复原mark-word解锁时会怎么办?

什么是偏向锁?

因为轻量锁,毕竟还是要每次都CAS,虽然CAS性能非常高。但这群“疯子”想还能不能优化。不要每次都CAS。而且轻量锁还涉及mark-word的copy,势必也会影响到GC。
但程序中很多场景,虽然要保证线程安全,但是80%的时间都不会有资源竞争。

此时偏向锁闪亮登场:
即当第一个线程来加锁时,通过一次CAS操作,将自己的threadId保存进markword中。只要成功就代表加锁成功。以后该线程再来加锁时,只要对比一下自己的threadId和markword中的threadId是不是一样就可以了。偏向 偏向 意思就是这个锁已经偏向给第一个线程了。

是不是一直用偏向锁呢,什么时候会被打破?
肯定不是啊,那不然还要轻量和重量锁啥用。当第二个线程来加锁时(不管第一个线程是否解锁),就宣告锁对象的偏向模式结束。就被升级为轻量或者重量。

这几种状态怎么流转?

前面说了很多都是文字。下面我通过一张图来描述一下整个流程怎么转换:
在这里插入图片描述

附上《深入理解jvm虚拟机》中的流程转换:
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

七层汉堡王

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

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

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

打赏作者

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

抵扣说明:

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

余额充值