高能再次提示
多年工作经验看下去,也同样会有收获!
因为,咱这个小伙伴的问题确实不简单!
小伙伴遇到的问题
Java学习交流群有个小伙伴,确实小,毕竟还是大学生,非常好学,当然不是妹子~
可爱猪猪可是不仅仅喜欢帮助妹子
先看他的代码:
类的命名,我们就忽略了,毕竟小伙伴是真的在练习和测试。随意起的类名。
类Test2
类Test2继承JFrame,是的,很古老的Java UI。忽略这个有年代感的类,瞧重点:
用Java画了一个窗口,窗口上有个Big Button,铺满窗口的。这个类有一个变量b默认false,并给按钮增加了一个事件,将b修改为true:
public boolean b = false;
...忽略其他代码
button.addActionListener(new ActionListener(){
// 按钮事件
public void actionPerformed(ActionEvent e){
// 修改b的值
b = true;
}
});
类Test1
类Test1继承的是一个线程,该线程进行死循环,判断test2.b==true,则打印Hello world:
public void run(){
while(true){
if(test2.b){
System.out.println("Hello world");
}
}
}
启动类Test2的main
main的实现逻辑是,实例化Test2,并传入Test1构造函数,启动Test1线程
public static void main(String[] args){
Test2 test2 = new Test2();
new Test1(test2).start();
}
小伙伴的疑问 ???
下图是这个代码启动后的全屏按钮:
小伙伴说:为什么启动后控制台没有打印Hello world?
我也一样看了几遍产生和小伙伴一样的疑问---Hello world跑哪去了呢?
一切都是那么天衣无缝!
这个结果才是我们期待的奇迹:
可是奇迹并未发生!
隐蔽的角落
Main线程修改了变量b的值,Test1线程读取变量b的值
b为mian线程和Test1线程共享变量 对,问题就处在共享变量上!!!!!
共享变量大家能够理解,毕竟共享时代也持续很久了~
可是这个变量真的共享吗?
既然这么问,那肯定不是真的!纳尼!!!!
CPU是如何访问内存变量
CPU能挖矿,我们知道性能特别快,如果频繁跨硬件访问主存,并且内存读写数据的速度要比CPU慢很多,如果是这样的话,那CPU再快也是浪费!
所以CPU访问内存变量采用了CPU内部的高速缓存来解决!
当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。
每个线程都有变量b的副本
标题如图:
按照程序逻辑走一走:
- 1.Main线程启动,初始化实例Test2,拷贝了变量b的副本到自己的高速缓存。
- 2.Test1线程启动,拷贝了变量b的副本到自己的高速缓存。
- 3.点击按钮,Main线程的高速缓存中b修改为true
- 4.然后Test1线程高速缓存什么都没做。b依然是false
@11 同学,现在能理解了吗?
缓存一致性
不用担心,你遇到的问题,伟人已经为我们铺好了路。
有两种方式解决线程间的缓存一致性,专业表述是这样的:
- 通过在总线加LOCK锁的方式
- 通过缓存一致性协议
CPU和其他部件通讯是通过总线完成的。这点先了解下。 看看下图:
其实上面两点通俗一点理解是这样的:
第1点可理解为,多个线程一个一个依次来读取或修改变量,一个读写,另外的就要等待,也就是阻塞。明显方案太out了。
伟人是接受不了的。第二点是通过一个xx协议完成。这个xx协议就是我没听说过的Intel 的MESI协议。 不想以后装b,可以不记下它的大名。
这个协议是干嘛的呢?一个线程如果修改了一个共享变量的值,注意是共享变量,如果其他cpu的高速缓存有这个共享变量的副本,那么就将它变成无效,如果其他CPU发现无效,就重新从主存读取。
TIP:如果觉得有点绕,可以读一遍。
我是个无辜的程序员,该怎么办呢?
使用Volatile关键字。姿势是这样的!
public volatile boolean b = false;
到这里,老司机可以离场,这个是面试80%必问题。
当然,公众号回复msgq1可以下载《面试怪圈内卷手册》会教给你怎么给面试官摆2小时的。
volatile关键字,有两个重要的作用:
- 保证线程间内存的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
- 防止指令重排
百度搜索volatile第一个,真心讲的不错,如果你有耐心花2个小时,把它读完:
回归正题了,还是说一下volatile内存可见性是怎么做到的:
1.如果Main线程修改了b的值为true
2.会导致线程Test1中高速缓存的b的值失效
3.Test1读取b的值得时候,发现已经失效,重新从主存中获取最新值。
好了,花了两个小时,解决了@11 朋友心中的困惑,希望他在编程的路上不再迷路。
不管怎么样,能够在大学的时候,遇到这样的问题,真的是给编程人生是很好的开始!
欢迎加入我的面试怪圈群,一起交流,成员不多,但是大佬很多,
也欢迎大家进入公众号-右下角圈儿,加我好友,谈谈你的程序人生。
我是公众号面试怪圈的可爱猪猪,我们下篇文章再见~
下载面试题可访问:www.mianshiguaiquan.com