最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分
public class VisibilityDemo {
private static volatile boolean flag = true;
public static void main(String… args) {
Thread thread1 = new Thread(() -> {
while (true) {
if (flag) {
flag = false;
System.out.println(“Thread1 set flag to false”);
}
}
}, “Thread-01”);
Thread thread2 = new Thread(() -> {
while (true) {
if (!flag) {
flag = true;
System.out.println(“Thread2 set flag to true”);
}
}
}, “Thread-02”);
// 分别启动两个线程
thread1.start();
thread2.start();
}
}
- 当共享变量 flag 为普通变量 private static boolean flag 时,程序中两线程会交替打印信息到控制台,一段时间后,两线程内部分支条件不再满足,将不再打印信息到控制台;
…
Thread2 set flag to true
Thread1 set flag to false
Thread1 set flag to false
Thread1 set flag to false
Thread2 set flag to true
Thread2 set flag to true
- 当共享变量由 volatile 修饰时 private static volatile boolean flag ,程序中两线程会持续交替打印信息到控制台;
…
Thread2 set flag to true
Thread1 set flag to false
Thread1 set flag to false
Thread1 set flag to false
Thread2 set flag to true
Thread2 set flag to true
…
3.1.3 可见性演示实例问题分析
由于线程工作内存与主内存存在缓存延时问题
一个普通的线程共享变量 private static boolean flag ,在上例中存在,随着程序的运行,在某个时刻线程 Thread-01 的 flag 为 false,线程 Thread-02 的 flag 为 true,此时两者都不会进入分支结构体,不再执行赋值操作,不再刷新工作内存数据到主内存。两个线程都会停止输出信息到控制台。
声明为 volatile 变量 private static volatile boolean flag ,会保证共享变量每次赋值都会即时存储到主内存,每次使用共享变量时,会从主内存读取并载入到当前线程工作内存再使用。使用关键字后的程序,两线程会持续交替输出信息到控制台。
============================================================================
===================================================================================
在当前线程观察 Java 程序,所有操作是有序的,但在其他线程观察当前线程的操作是无序的。即线程内表现为串行的语义,多线程间存在工作内存与主内存同步延时及指令重排序现象。
===================================================================================
- 多线程下指令重排的线程安全问题
我们知道处理器在指令集层面,会做一定的指令排序优化,来提升处理器运算速度。在单线程中可以保证对应高级语言的程序执行结果是正确的,即单线程下保证程序执行的有序性(及程序正确性);多线程情况下,在某个线程中观察其他线程的操作是无序的(存在线程共享内存时,则无法保证程序正确性),这就是多线程下指令重排的线程安全问题。
4.2.1 指令重排演示实例
import lombok.SneakyThrows;
/**
-
@description: 指令重排:线程内表现为串行语义
-
@author: niaonao
**/
public class OrderRearrangeDemo {
static boolean initFlag;
public static void main(String… args) {
Runnable customRunnable = new CustomRunnable();
new Thread(customRunnable, “Thread-01”).start();
new Thread(customRunnable, “Thread-02”).start();
}
static class CustomRunnable implements Runnable {
// @SneakyThrows 是 lombok 包下的注解
// 继承了 Throwable 用于捕获异常
@SneakyThrows
@Override
public void run() {
initFlag = false;
Integer number = null;
number = 1;
initFlag = true;
// 等待初始化完成
while (!initFlag) {
}
System.out.println("name: " + Thread.currentThread().getName() + ", number: " + number);
}
}
}
上面这个例子,在实际并发场景中很少出现线程安全问题,但存在指令重排引起线程安全问题的风险。
- 一般情况下执行结果为
name: Thread-01, number: 1
name: Thread-02, number: 1
Process finished with exit code 0
- 指令重排存在的风险结果可能为
name: Thread-01, number: 1
name: Thread-02, number: null
Process finished with exit code 0
4.2.2 指令重排演示实例问题分析
线程内保证程序的有序性,多线程下处理器指令重排优化存在的情况如下(这里从高级语言来快速理解,其实指令我也做不到啊),下面并没有列出所有情况。
// 情况-01
initFlag = false;
Integer number = null;
number = 1;
initFlag = true;
while (!initFlag) {
}
System.out.println("name: " + Thread.currentThread().getName() + ", number: " + number);
// 情况-02
initFlag = false;
Integer number = null;
initFlag = true;
number = 1;
while (!initFlag) {
}
System.out.println("name: " + Thread.currentThread().getName() + ", number: " + number);
// 情况-03
initFlag = false;
initFlag = true;
Integer number = null;
number = 1;
while (!initFlag) {
}
System.out.println("name: " + Thread.currentThread().getName() + ", number: " + number);
// 情况-04
Integer number = null;
initFlag = false;
number = 1;
initFlag = true;
while (!initFlag) {
}
System.out.println("name: " + Thread.currentThread().getName() + ", number: " + number);
// 情况-05
Integer number = null;
initFlag = false;
initFlag = true;
number = 1;
while (!initFlag) {
}
System.out.println("name: " + Thread.currentThread().getName() + ", number: " + number);
// 情况-06
Integer number = null;
number = 1;
initFlag = false;
感受:
其实我投简历的时候,都不太敢投递阿里。因为在阿里一面前已经过了字节的三次面试,投阿里的简历一直没被捞,所以以为简历就挂了。
特别感谢一面的面试官捞了我,给了我机会,同时也认可我的努力和态度。对比我的面经和其他大佬的面经,自己真的是运气好。别人8成实力,我可能8成运气。所以对我而言,我要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。
也祝愿各位同学,都能找到自己心动的offer。
分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档
要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。
也祝愿各位同学,都能找到自己心动的offer。
分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档
[外链图片转存中…(img-HkaYzWfx-1715687390135)]