面试官:你知道并发Bug的源头是什么吗?

简介: 面试官:你知道并发Bug的源头是什么吗?

讲实话听到这个问题,不太熟悉并发编程的同学有点晕,你可能只能答个因为多线程之间的竞争共享资源啊。对说的没错。


但是呢感觉不够亮眼!我们的目的就是让面试官眼前一亮,让他颤抖!心里鼓掌:"牛批牛批!"


image.png

多线程,给我们的感觉像啥?就像《风云》里面的无名的"万剑归宗"!《雪中悍刀行》里面老剑神李淳罡一声“剑来!”。哗哗哗的好多剑同时操作着一起飞!多帅哦!


但实际上CPU的执行是有时间片的,也就是其实我们多线程不是同一时间一起执行的,是每个线程执行一会儿就换一个上,但是时间片很短,所以我们感觉上是一起执行的。当然多核CPU的话,每个CPU是并行的,但是我们CPU颗数哪有我们的线程多!


大家都知道我们电脑有CPU,内存,IO设备。IO读取时最慢的,然后是内存读取,最快的是CPU的执行。那因为内存太慢了,跟不上CPU,所以CPU搞了个缓存。就是因为这个缓存再加上多颗CPU并发就出问题了。


举个例子

int addOne(){
             a=a+1;
        }

上面这个程序很简单,但是如果有两个线程A和B,分别CPU-A和CPU-B中同时addOne()可能会出现什么问题呢?



image.png

image.png

同时的线程A把a从内存中取到CPU-A缓存中,缓存中a的值是0,线程B把a从内存中取到CPU-B缓存中缓存中a的值是0。然后它们各自+了个1。这时候在它们的各自的缓存中a的值都是1。那之后写入内存中,是不是a就变成1了?


那我们加了两次是不是少了一次了!这是问题就是可见性问题了!CPU-A的缓存的值CPU-B不可见!这就是可见性的根本原因。


我们再来说一下原子性,首先原子性指的就是一个或多个操作在CPU中执行不会被中断的特性称为原子性。


我们的JAVA语言是高级语言。高级语言是怎么个情况。就是我们一条java代码涵盖了好几条底层指令。比如我们上面的a=a+1;把它转换成CPU指令至少有三条。


第一条:把a从内存拿到寄存器中;


第二条:寄存器中+1;


第三条:结果写入缓存或内存中;


那按照CPU的原子性,它是指令级别的!一条指令是不会被中断的,所以说在我们以为a=a+1;是原子级别的,实际上在CPU看来不是。


所以呢如果此时有两个线程同时执行,那可能线程A执行到第二条的时候,时间片到了。现在换线程B上了,然后线程B又把a从内存中拿到,然后+1返回结果,此时换到了线程A,线程A继续执行第三条。那情况就是又错了。


还没完呢!我们编译器或解释器的优化可能带来意料不到的问题!为了优化性能,它可能会改变语句执行的顺序,也就是指令重排!


最经典的就是单例的双重检查了!


image.png

看起来都加锁了好像没问题了,而问题就出在 instance = new Singleton(); 上

我们认为的new操作就是:


1.分配一块内存
    2.在内存上初始化Singleton
    3.将内存地址赋值给instance


而指令重排之后:


1.分配一块内存
    2.将内存地址分配给instance
    3.初始化Singleton


这会导致什么问题呢?假设线程A已经执行到new Singleton()的指令2了,然后时间片到了,这时候线程B也调用getInstance();到第一个instance==null时候,直接返回了。很开心拿到了对象了!而实际上对象还没初始化呢!所以用了的话就是空指针了!

这就是有序性问题了!


所以一共有三大特性:可见性、原子性、有序性。并发的问题都逃不过这三大特性!JAVA的内存模型其实有很多都是解决这几种问题的,比如上面单例的例子instance由 volatile来声明一下,这样就禁止指令重排了!

希望通过今天的分析大家能深入理解这三大特性!写出健壮的并发代码!



相关文章
|
12天前
|
缓存 NoSQL 关系型数据库
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
本文详解缓存雪崩、缓存穿透、缓存并发及缓存预热等问题,提供高可用解决方案,帮助你在大厂面试和实际工作中应对这些常见并发场景。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
大厂面试高频:如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难题
|
3月前
|
并行计算 数据挖掘 大数据
[go 面试] 并行与并发的区别及应用场景解析
[go 面试] 并行与并发的区别及应用场景解析
|
1月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
27 0
|
3月前
|
Java 程序员 调度
面试准备-并发
面试准备-并发
|
3月前
|
消息中间件 Java 中间件
复盘女朋友面试4个月的并发面试题
该文章主要复盘了关于并发的面试题,包括线程池的使用场景、原理、参数合理化设置,以及ThreadLocal、volatile、synchronized关键字的使用场景和原理,还介绍了juc并发工具包中aqs的原理,强调在面试中要将自己理解的点与面试官讲透。
复盘女朋友面试4个月的并发面试题
|
3月前
|
JavaScript 前端开发 Java
面试官:假如有几十个请求,如何去控制并发?
面试官:假如有几十个请求,如何去控制并发?
|
3月前
|
安全 Go 调度
[go 面试] 深入理解并发控制:掌握锁的精髓
[go 面试] 深入理解并发控制:掌握锁的精髓
|
3月前
|
算法 Go 数据库
[go 面试] 并发与数据一致性:事务的保障
[go 面试] 并发与数据一致性:事务的保障
|
3月前
|
NoSQL Go API
[go 面试] 为并发加锁:保障数据一致性(分布式锁)
[go 面试] 为并发加锁:保障数据一致性(分布式锁)
|
4月前
|
设计模式 安全 Java
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
Java面试题:设计模式如单例模式、工厂模式、观察者模式等在多线程环境下线程安全问题,Java内存模型定义了线程如何与内存交互,包括原子性、可见性、有序性,并发框架提供了更高层次的并发任务处理能力
78 1