syn关键字

syn关键字

以前做数据同步是使用syn关键字加锁的,直到juc包的出现,有了cas操作,锁操作效率变高

markword里记录了锁.

锁升级步骤

1,new 普通对象

2.加了syn关键字后,偏向锁

3,一旦竞争激烈,就变成轻量级锁(自旋锁),也有一种可能是轻量级锁未启动直接上升到轻量级锁

4.竞争再加剧,重量级锁(向操作系统申请)

看对象分布的第一组0000 0000的倒数两位

0 0 1 :刚new的

1 0 1:偏向锁

0 0:轻量级

1 0:重量级

偏向锁

偏向锁和轻量级锁是用户空间锁,不需要和操作系统交互,比如stringbuffer,里面所有的方法都加上了syn关键字,但是大多数情况只有一个线程在调用无竞争,如果syn没优化的话,每次调用都要向操作系统申请锁,效率极低.所以哪个线程先来就偏向它.把线程id写到markword上(c++实现里是当前线程指针).

前几位是当前线程指针,后几位是锁标志位

但是当有了锁竞争后,偏向锁就被撤下了切换成自旋锁,偏向锁时,有其他线程来竞争锁,则先把 偏向锁撤销,然后进行 自旋锁(轻量级锁)竞争,所以会有锁撤销性能损耗

自旋锁

开始竞争,每个线程会在自己的线程栈中生成一个叫 LR的Lock Record,用CAS的方式将LR指针设置到锁对象的markword上

前几位是指向线程栈的LR

1.在线程栈中创建一个Lock Record,将其obj(即上图的Object reference)字段指向锁对象。
2.直接通过CAS指令将Lock Record的地址存储在对象头的mark word中,如果对象处于无锁状态则修改成功,代表该线程获得了轻量级锁。如果失败,进入到步骤3。
3.如果是当前线程已经持有该锁了,代表这是一次锁重入。设置Lock Record第一部分(Displaced Mark Word)为null,起到了一个重入计数器的作用。然后结束。

  • JVM虚拟机在每一个竞争线程的栈帧中,建立一个自己的 **锁记录 (Lock Record, LR) **空间,存储锁对象目前 markword 的拷贝.就相当于修改某变量,需要把它复制到线程工作空间一样
  • 。竞争线程 使用 CAS 的方式,尝试把被竞争对象的 markword 更新为指向竞争线程 LR 的指针,如果更新成功即代表该线程拥有了锁,锁标志位将转变为 00,表示处于轻量级锁定状态。
  • CAS 是一种乐观锁:cas(v, a, b) 变量v,期待a,修改值b
  • Java 中调用了 native 的 compareAndSwapXXX() 方法
  • 每个人在自己的线程内部生成一个自己LR(Lock Record锁记录),两个线程通过自己的方式尝试将 LR 写门上,竞争成功的开始运行,竞争失败的一直自旋等待。
  • 实际上是汇编指令 lock cmpxchg,硬件层面实现。

重量级锁

向os申请锁,markword中保存objectmonitor

    public synchronized void trimToSize() {
        
    }

汇编层次(不明)

这个方法在jvm汇编中会被解析成一个monitorenter的指令与monitorexit.

monitorenter在cpp中的实现,如果使用了偏向锁就快速拿到锁fast enter,如果没有偏向锁就慢的获得slow enter.

fast enter首先如果偏向锁获得成功那就成功,如果偏向锁获得失败,还是会进入slow enter

slow enter是使用atomic::cmpxchg,升级为轻量级锁(自旋锁)

等monitorenter结束就获取锁了

锁重入

syn必须是可重入的,可以加一次锁,可重入次数必须记录下来,因为要解锁必须要对应加锁次数

LR lock record,重入计数器

偏向锁的重入次数记录在线程栈里,LR再生成一个,解锁就弹出一个LR

轻量级锁升级重量级

自旋的线程超过cpu核心数的一半就要升级重量级了.因为自旋锁会一直自旋,消耗性能.1.6之后由jvm控制

为什么有自旋锁还需要重量级锁

因为自旋锁一直运行,cpu压力大,自旋这件事是占有cpu时间的,如果锁的时间长,自旋线程多,cpu资源被大量消耗,执行空循环,空切换!!

升级成重量级锁的话,重量级的锁objectmonitor.cpp文件中写了objectmonitor的实现,其中有waitset队列.当某个线程要向os申请锁时,锁会把它扔到队列里去,把它阻塞住不让它执行

偏向锁未启动

问题:偏向锁是不是一定比自旋锁效率高

不是,只有一个线程的时候效率高,当多个线程的时候不一定效率高

偏向锁会涉及锁撤销,明知道有很多线程竞争的话应该直接关闭偏向锁

jvm启动过程中会有很多线程竞争,所以默认启动不打开偏向锁,启动完了再打开

轻量级锁不会wait,直接升级重量级锁

面试问

  • 1.基本用法
  • 2.实现原理
    • 2.1 同步代码块的实现
    • 2.2 同步方法的实现
  • 3.锁升级
    • 3.1 Java对象头介绍
    • 3.2 什么是锁升级

syn通过jvm汇编monitorenter和monitorexit保证线程安全

首先object的对象头markword保存着关于锁的信息,

在 monitorenter 函数内部的实现为:如果打开了偏向锁,则进入 fast_enter, 在 safepoint情况下,尝试获取偏向锁,成功则返回,失败则进入 slow_enter, 升级为自旋锁,如果自旋锁失败,则膨胀 inflate 成为重量级锁。重量级锁的代码在 syncronizer.cpp 中,里面调用了 linux 内核的一些实现方法。

每个对象都与一个 monitor 相关联。当且仅当拥有所有者时(被拥有),monitor才会被锁定。执行到monitorenter指令的线程,会尝试去获得对应的 monitor,如下:

每个对象维护着一个记录着被锁次数的计数器, 对象未被锁定时,该计数器为0。线程进入monitor(执行monitorenter 指令)时,会把计数器设置为1。当同一个线程再次获得该对象的锁的时候,计数器再次自增,这就是锁重入。当其他线程想获得该 monitor 的时候,就会阻塞,直到计数器为0才能成功。

  • X86 : lock cmpxchg / xxx
  • lock 是处理多处理器之间的总线锁问题

Object Monitor机制就是synchronized同步块锁机制升级为“重量级锁”后的工作机制,

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值