【牛客每日面经】—— 6.27

猿辅导 服务端一面

在这里插入图片描述

volatile 是干什么的?在内存层面上是在怎么实现的?使用场景是什么?

并发编程中有三大特性:1. 原子性,一个操作或者多个操作,要么全部执行成功,要么全部执行失败。2. 可见性,多个线程共同访问共享变量时,某个线程修改了该变量,其他线程能立即看到修改后的值。3. 有序性,程序执行的顺序按照代码的先后顺序执行。

(一)volatile有两个作用

  1. 保证变量的可见性,但是不保证原子性
  2. 防止变量的指令重排序

但是synchronized关键字同时保证上面并发的三种特性:因为synchronized是同步锁,不存在原子性和指令重排序的问题,而且线程解锁前,必须把共享变量的最新值刷新到主内存中。

volatile的两大作用解释

  1. 可见性
    Java虚拟机规定了一种Java内存模型来屏蔽各种硬件和操作系统的差异,以实现Java程序在各种平台下能达到一致的并发效果。
    JMM中规定所有的变量都存储在主内存(Main Memory)中,每条线程都有自己的工作内存(Work Memory),线程的工作内存中保存了该线程所使用的变量的从主内存中拷贝的副本。线程对于变量的读、写都必须在工作内存中进行,而不能直接读、写主内存中的变量。同时,本线程的工作内存的变量也无法被其他线程直接访问,必须通过主内存完成。
  2. 指令重排序
    volatile通过编译器生成字节码时,在指令序列中添加内存屏障来禁止指令重排序。
    硬件层面的内存屏障:
    lock锁
    JMM层面的内存屏障:
    loadload屏障
    storestore屏障
    loadstore屏障
    storeload屏障

(二)volatile的底层实现

从Java代码、字节码、Jdk源码、汇编层面、硬件层面

  • 代码层面:volatile用来修饰java变量
  • 字节码层面:使用javap可以查看反编译字节码文件,可以发现volatile在字节码层面,就是使用访问标志:ACC_VOLATILE来表示,供后续操作此变量时判断访问标志是否为ACC_VOLATILE,来决定是否遵循volatile的语义处理。
  • volatile采用lock前缀,会保证某个处理器对共享内存(一般是缓存行cacheline,这里记住缓存行概念,后续重点介绍)的独占使用。它将本处理器缓存写入内存,该写入操作会引起其他处理器或内核对应的缓存失效。通过独占内存、使其他处理器缓存失效,达到了“指令重排序无法越过内存屏障”的作用

(三)使用场景
synchronized关键字防止多个线程同时执行一段代码,会影响执行效率,而volatile关键字性能优于synchronized,但是无法保证原子性。
场景:1. 单例模式中的双重检查,【如果不用volatile,内存模型会允许无序写入,可能线程会获得一个未完全初始化的实例】
2. 状态标记量,用来知识发生了一个重要的一次性时间,例如完成初始化或请求停机
volatile的使用场景参考

synchronized 使用方法,底层是怎么实现的

有三种使用方法

  • 修饰实例方法:作用于当前对象是加锁,进入同步代码前要获得当前实例的对象
  • 修饰静态方法,即给类加锁,会作用于类的所有对象实例,进入同步代码前要获得当前class锁
  • 修饰代码块,指定加锁对象,对给定对象/类加锁

底层实现

可以通过JDK自带的javap查看类的相关字节码信息

synchronized同步语句块的实现,使用的是monitorenter和monitorexit指令,其中monitorrnter指令执行同步代码块的开始位置,monitorexit指令则指明同步代码块的结束位置。
在执行monitorenter时,会获得对象的锁,如果锁的计数器为0,则表示锁可以被获取,获取后锁的计数器进行加1,
在执行monitorexit指令后,将锁计数器设为0,表明锁释放,如果获取对象锁失败,则当前线程阻塞,直到锁被另一个线程释放为止。

synchronized修饰方法,没有monitorenter和monitorexit指令,而是用ACC_SYNCHRONIZED标识,指明该方法是一个同步方法。JVM通过ACC_SYNCHRONIZED访问标志来辨别一个方法是否声明为同步方法,从而执行相应的同步调用。

两者的本质都是对对象监视器monitor的获取。

Java对 synchronized 有什么 改进? (偏向锁,轻量锁,重量锁)

synchronized 锁升级的过程:
在锁对象的对象头里面有一个 threadid 字段,未访问时 threadid 为空
第一次访问 jvm 让其持有偏向锁,并将 threadid 设置为其线程 id
再次访问时会先判断 threadid 是否与其线程 id 一致。如果一致则可以直接使用此对象;如果不一致,则升级偏向锁为轻量级锁,通过自旋循环一定次数来获取锁
执行一定次数之后,如果还没有正常获取到要使用的对象,此时就会把锁从轻量级升级为重量级锁。

锁升级的目的是为了减低锁带来的性能消耗,在 Java 6 之后优化 synchronized 为此方式。

在Java中,锁共有4种状态,级别从低到高依次为:无状态锁,偏向锁,轻量级锁和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级。

锁的升级是通过对象头中的一个标志位来判断,两个位置00表示的4种锁。

TCP 拥塞算法

TCP协议有两个比较重要的控制算法,一个是流量控制,另一个就是阻塞控制。

TCP协议通过滑动窗口来进行流量控制,它是控制发送方的发送速度从而使接受者来得及接收并处理。而拥塞控制是作用于网络,它是防止过多的包被发送到网络中,避免出现网络负载过大,网络拥塞的情况。

拥塞算法需要掌握其状态机和四种算法。拥塞控制状态机的状态有五种,分别是Open,Disorder,CWR,Recovery和Loss状态。四个算法为慢启动,拥塞避免,拥塞发生时算法和快速恢复。

在这里插入图片描述

在这里插入图片描述
参考:TCP拥塞控制算法

Spring AOP 是干什么的? 使用场景? 底层实现?

AOP的作用

作用:在不修改源代码的情况下,可以实现功能的增强。

AOP 思想: 基于代理思想,对原来目标对象,创建代理对象,在不修改原对象代码情况下,通过代理对象,调用增强功能的代码,从而对原有业务方法进行增强 !

AOP应用场景

场景一: 记录日志
场景二: 监控方法运行时间 (监控性能)
场景三: 权限控制
场景四: 缓存优化 (第一次调用查询数据库,将查询结果放入内存对象, 第二次调用, 直接从内存对象返回,不需要查询数据库 )
场景五: 事务管理 (调用方法前开启事务, 调用方法后提交关闭事务 )

AOP的实现原理

那Spring中AOP是怎么实现的呢?

Spring中AOP的有两种实现方式:
1、JDK动态代理,对实现了接口的类进行增强
2、Cglib动态代理,生成了目标类的子类来增强

Mysql 引擎都有啥? 聚簇索引 和 非聚簇索引 是怎么实现的? B 树和 B+ 树有什么区别? 为什么B + 树能让搜索变得更快?

Mysql 事务隔离级别? 幻读是什么? 怎么才能解决幻读

知道Mysql 锁的粒度么? 什么是 GAP Lock

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值