最新高效并发下的高速缓存和指令重排(1),Java组件化开发教程

最近我根据上述的技术体系图搜集了几十套腾讯、头条、阿里、美团等公司21年的面试题,把技术点整理成了视频(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

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 ,会保证共享变量每次赋值都会即时存储到主内存,每次使用共享变量时,会从主内存读取并载入到当前线程工作内存再使用。使用关键字后的程序,两线程会持续交替输出信息到控制台。

4. 指令重排

============================================================================

4.1 就你TMD叫指令重排啊

===================================================================================

在当前线程观察 Java 程序,所有操作是有序的,但在其他线程观察当前线程的操作是无序的。即线程内表现为串行的语义,多线程间存在工作内存与主内存同步延时及指令重排序现象。

4.2 指令重排的线程安全问题

===================================================================================

  • 多线程下指令重排的线程安全问题

我们知道处理器在指令集层面,会做一定的指令排序优化,来提升处理器运算速度。在单线程中可以保证对应高级语言的程序执行结果是正确的,即单线程下保证程序执行的有序性(及程序正确性);多线程情况下,在某个线程中观察其他线程的操作是无序的(存在线程共享内存时,则无法保证程序正确性),这就是多线程下指令重排的线程安全问题。

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后,简历被阿里捞了起来,二面迎来了P9"盘问"

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

要继续加倍努力,弥补自己技术上的不足,以及与科班大佬们基础上的差距。希望自己能继续保持学习的热情,继续努力走下去。

也祝愿各位同学,都能找到自己心动的offer。

分享我在这次面试前所做的准备(刷题复习资料以及一些大佬们的学习笔记和学习路线),都已经整理成了电子文档

[外链图片转存中…(img-HkaYzWfx-1715687390135)]

本文已被CODING开源项目:【一线大厂Java面试题解析+核心总结学习笔记+最新讲解视频+实战项目源码】收录

需要这份系统化的资料的朋友,可以点击这里获取

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值