当前位置: 首页>JAVA面试题>超牛逼的面试八股文

超牛逼的面试八股文

作者:你好吗 4月前 ⋅ 391 阅读

 

1.熟悉Synchronized吗?
synchronized关键字解决的是多个线程之间访问资源的同步性,
synchronized关键字可以保证被它修饰的方法或者代码块在任意时刻只能有一个线程执行。

2.锁升级过程了解吗?
2.1,偏向锁:同步只有一个线程使用,使用过程中不存在竞争
2.2,轻量锁:第一个线程进入同步块后,第二个线程来竞争锁,此时会增加在调用栈记录mark word的锁信息(对象中mark指向其),
如果由于第二个线程的竞争导致栈中的mark word更新失败,
并且目前对象中的mark不指向当前线程栈中的mark就会升级为重量锁(第二个线程会修改栈中的记录,尝试修改成功),
第二个线程不会阻塞会不断的尝试修改(一定时间内),自旋成功获取锁,此时还是自旋锁,自选失败升级为重量锁,阻塞
2.3,重量锁


3.Java内存模型了解吗?
Java平台自动集成了线程以及多处理器技术,这种集成程度比Java以前诞生的计算机语言要厉害很多,
该语言针对多种异构平台的平台独立性而使用的多线程技术支持也是具有开拓性的一面,
有时候在开发Java同步和线程安全要求很严格的程序时,往往容易混淆的一个概念就是内存模型。
究竟什么是内存模型,内存模型描述了程序中各个变量(实例域、静态域和数组元素)之间的关系,
以及在实际计算机系统中将变量存储到内存和从内存中取出变量这样的底层细节,对象最终是存储在内存里面的,
这点没有错,但是编译器、运行库、处理器或者系统缓存可以有特权在变量指定内存位置存储或者取出变量的值。
【JMM】(Java Memory Model的缩写)允许编译器和缓存以数据在处理器特定的缓存(或寄存器)和主存之间移动的次序拥有重要的特权,
除非程序员使用了volatile或synchronized明确请求了某些可见性的保证。


4.说一下锁的内存语义?
当线程释放锁时,JMM(Java内存模型)把该线程对应的本地内存中的共享变量刷新到主内存中。
当线程获取锁时,JMM会把该线程对应的本地内存置为无效。从而使得被监视器保护的临界区代码必须从主内存中读取共享变量。
对比锁释放-获取的内存语义与volatile写-读的内存语义可以看出:锁释放与volatile写有相同的内存语义;锁获取与volatile读有相同的内存语义。
线程A释放一个锁,实质上是线程A向接下来将要获取这个锁的某个线程发出了(线程A对共享变量所做修改的)消息。
线程B获取一个锁,实质上是线程B接收了之前某个线程发出的(在释放这个锁之前对共享变量所做的修改的)消息。
线程A释放锁,随后线程B获取这个锁,这个过程实质上是线程A通过主内存向线程B发送消息。

5.为什么要重排序?
(1)编译器优化
编译器(包括 JVM、JIT 编译器等)出于优化的目的,例如当前有了数据 a,把对 a 的操作放到一起效率会更高,避免读取 b 后又返回来重新读取 a 的时间开销,
此时在编译的过程中会进行一定程度的重排。不过重排序并不意味着可以任意排序,它需要需要保证重排序后,不改变单线程内的语义,否则如果能任意排序的话,程序早就逻辑混乱了。
(2)CPU 重排序
CPU 同样会有优化行为,这里的优化和编译器优化类似,都是通过乱序执行的技术来提高整体的执行效率。
所以即使之前编译器不发生重排,CPU 也可能进行重排,我们在开发中,一定要考虑到重排序带来的后果。
(3) 内存的“重排序”
内存系统内不存在真正的重排序,但是内存会带来看上去和重排序一样的效果,所以这里的“重排序”打了双引号。
由于内存有缓存的存在,在 JMM 里表现为主存和本地内存,而主存和本地内存的内容可能不一致,所以这也会导致程序表现出乱序的行为。
举个例子,线程 1 修改了 a 的值,但是修改后没有来得及把新结果写回主存或者线程 2 没来得及读到最新的值,
所以线程 2 看不到刚才线程 1 对 a 的修改,此时线程 2 看到的 a 还是等于初始值。但是线程 2 却可能看到线程 1 修改 a 之后的代码执行效果,表面上看起来像是发生了重顺序。
好处:提高处理速度

6.讲一下指令调度?
​ 指令调度是编译优化中用于提高指令级并行,从而提高在计算机上指令流水线的性能。更直接的说,在没有改变原代码语义的情况下,它做了下面两件事:
1. 通过重排指令顺序避免指令流水线停顿;
2. 避免非法或语义模糊的操作(涉及典型的细微的指令流水线时序问题或非互锁的资源);
​ 指令流水线停顿可能是由结构危险(处理器资源限制),数据危险(输出另一条指令所需的一条指令),控制危险(分支);
​ 为了能产生快速的代码,代码生成阶段重排指令,以照顾目标机器在特定方面的性能约束。
​ 不同操作指令的执行时间可能是不同的。内存访问操作可能需要花费几十甚至数百个CPU周期,
但某些算术操作(除除法为例),需要几个CPU周期。这种延迟较长的