Synchronized马士兵底层剖析

Synchronized剖析

在这里插入图片描述

Object o = new Object();

在这里插入图片描述

synchronizerd对象头的锁

synchronized的信息是记录在对象头中MarkWord

在这里插入图片描述

synchronized锁升级过程

new-->偏向锁-->轻量级锁(无锁,自旋锁,自适应锁)-->重量级锁
synchronized优化的过程和markdown息息相关
用Markdown中最低的3位代表锁状态,其中1位是锁偏向状态,两位是普通锁位

偏向锁(只有一个线程访问锁)

在这里插入图片描述

偏向锁的核心思想是,如果一个线程获得了锁,那么锁就进入偏向模式,此时Mark Word 的结构也变为偏向锁结构,当这个线程再次请求锁时,无需再做任何同步操作,即获取锁的过程,这样就省去了大量有关锁申请的操作,从而也就提供程序的性能。所以,对于没有锁竞争的场合,偏向锁有很好的优化效果,毕竟极有可能连续多次是同一个线程申请相同的锁。但是对于锁竞争比较激烈的场合,偏向锁就失效了,因为这样场合极有可能每次申请锁的线程都是不相同的,因此这种场合下不应该使用偏向锁,否则会得不偿失,需要注意的是,偏向锁失败后,并不会立即膨胀为重量级锁,而是先升级为轻量级锁。

轻量级锁/自旋锁(多个线程抢)

在这里插入图片描述

  • 如果多个锁竞争资源则偏向锁失效,虚拟机并不会立即挂起线程,它还会使用一种称为轻量级锁的优化手段。轻量级锁的操作也很轻便,它只是简单的将对象头部作为指针指向持有锁的线程堆栈的内部,来判断一个线程是否持有锁。
  • 轻量级锁失败后,虚拟机为了避免线程真实地在操作系统层面挂起,还会进行一项称为自旋锁的优化手段。这是基于在大多数情况下,线程持有锁的时间都不会太长,如果直接挂起操作系统层面的线程可能会得不偿失,毕竟操作系统实现线程之间的切换时需要从用户态转换到核心态,这个状态之间的转换需要相对比较长的时间,时间成本相对较高,因此自旋锁会假设在不久将来,当前的线程可以获得锁,因此虚拟机会让当前想要获取锁的线程做几个空循环(这也是称为自旋的原因),一般不会太久,可能是50个循环或100循环,在经过若干次循环后,如果得到锁,就顺利进入临界区。如果还不能获得锁,那就会将线程在操作系统层面挂起,这就是自旋锁的优化方式,这种方式确实也是可以提升效率的。最后没办法也就只能升级为重量级锁了。
  • 线程进行抢锁,就升级为轻量级锁;这里自旋,处于用户态
    每个线程在自己的栈帧中生成一个Lock Record然后在while()循环中自旋的获取锁,若获取则将线程栈中的指针指向该线程的Lock Record;其余的线程自旋去抢,但是若自旋的次数特别多,则浪费CPU,当自旋到一定次数的时候,则会再次升级为重量级锁

重量级锁(用户太升级到内核态,操作系统互斥量)

在这里插入图片描述
在这里插入图片描述
当竞争加剧,自旋的次数特别多还没有获取到轻量级锁的时候,则升级到这种锁,向CPU申请互斥量:重量级锁;
互斥量是内核态中互斥的数据机构,申请重量锁即锁的指针指向的是内核态中的指针

锁消除

消除锁是虚拟机另外一种锁的优化,这种优化更彻底,Java虚拟机在JIT编译时(可以简单理解为当某段代码即将第一次被执行时进行编译,又称即时编译),通过对运行上下文的扫描,去除不可能存在共享资源竞争的锁,通过这种方式消除没有必要的锁,可以节省毫无意义的请求锁时间

锁粗化

在这里插入图片描述

synchronized底层实现原理剖析

java代码层级:synchronized

  • 同步方法块: 对给定对象加锁,进入同步代码前需要获得给定对象的锁
  • 同步方法: 锁是当前实例对象
  • 静态同步方法:锁是自己定义的锁对象

jvm字节码层级:monitorenter和monirorexit指令

monitorenter指令插入到同步代码块的开始位置,monitorexit指令插入到同步代码块的结束位置,JVM需要保证每一个monitorenter都有一个monitorexit与之相对应。任何对象都有一个monitor(在对象头中)与之相关联,当且一个monitor被持有之后,他将处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor所有权,即尝试获取对象的锁;

jvm执行过程中锁升级:

偏向级锁-->轻量级锁(自旋锁)---->重量级锁

CPU汇编层级:lock comxchg

底层机器吗最终还是Lock cmpxgahage(和volitale是一样的)

volitale

计算机基础

缓存一致性,系统底层如何实现数据一致性

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 缓存一致性协议能解决,就使用MESI
  • 如果不能,就锁总线

程序顺序性/乱序执行:系统底层如何保证有序性

在这里插入图片描述

  • 内存屏障:sfence mfence lfence等系统原语
  • 锁总线

volitale如何解决指令重排序

java层 voiliate i

volitale ing i

字节码层级

ACC_VOLATILE就是一个标志

虚拟机规范

内存屏障两边额指令不快乐同一重排,保证有序
在这里插入图片描述
在这里插入图片描述

hotspot实现

在这里插入图片描述

最终还是用的lock指令,和synchronized,cas一致

ThreadLocal

四种引用

软引用

SoftRerence <byte[]> m = new Reference<>(new byte[1024*1024]);

在这里插入图片描述

当内存不够时,软引用的对象会回收。
缓存中用的比较多。

弱引用WeakReference(ThreadLocal)

在这里插入图片描述

只要垃圾回收就回收

虚引用(管理堆外内存)


PhantomReference<M> phantomReference = new PhantomReferecne<>(new M(),QUEUE);

作用:管理堆外内存
在这里插入图片描述

ThreadLocal介绍

绑定在每个线程上的对象

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

为什么是弱引用????(内存泄露
一旦外部对ThreadLocal的引用消失,则Entry指向的ThreadLocal因为是弱引用则可以对ThreadLocal回收,此时key为null,我们需要手动进行remove()方法

应用

Spring的@Transcational注解中,多个数据库连接确保是一样的需要使用ThreadLocal

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值