当前位置:网站首页>【面试:并发篇25:多线程:volatile】可见性
【面试:并发篇25:多线程:volatile】可见性
2022-07-25 21:27:00 【I cream】
【面试:并发篇25:多线程:volatile】可见性
00.前言
如果有任何问题请指出,感谢。
01.介绍
可见性是值,一个线程对共享变量修改另一个线程可以看到最新的结果
02.例子
@Slf4j(topic = "c.Test32")
public class Test32 {
static Boolean stop = false;
public static void main(String[] args) {
new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true;
log.debug("把stop修改为true");
}).start();
foo();
}
static void foo(){
int i=0;
while (!stop){
i++;
}
log.debug("stopped...c:{}",i);
}
}
结果
程序没有结束:
18:44:55.991 c.Test32 [Thread-0] - 把stop修改为true
解释
上述代码表达的意思是,我们创建了一个stop成员变量默认为false 我们创建一个方法foo 它里面有一个while循环 只有stop为true时 它才能跳出循环,然后 我们创建了一个线程 我们在它里面使stop变为true,我们使线程在0.1s后再执行stop=true,所以理论上来说 我们在0.1s后 while循环就应该退出并打印循环次数了,可是我们却发现 这个循环并没有退出 也就是,对于while循环来说此时stop还是false
03.对于上述例子的解释
错误解释
在网上有这么一种解释,下图为JMM(java内存模型)图,
注意:下文中的高速缓存 副本 工作内存指的是一个东西
即每个线程对共享变量进行操作时 这个线程就会把主存中的共享变量复制一份到高速缓存中,之后线程的每次操作都是在高速缓存中执行 加快效率。就像上图t0线程复制了一个副本 并修改其共享变量副本,此时t1读取的还是它自己的共享变量副本 导致读取错误。对于上述例子就是因为t0创建了一个stop的副本 然后进行了修改stop=true,然后t1执行while(!stop)时读取的还是自己的副本 也就是主存的stop=false,所以导致循环不能结束。但这个解释是错误的。
为什么上述解释是错误的
@Slf4j(topic = "c.Test32")
public class Test32 {
static Boolean stop = false;
public static void main(String[] args) {
new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true;
log.debug("把stop修改为true");
}).start();
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("{}",stop);
}).start();
foo();
}
static void foo(){
int i=0;
while (!stop){
i++;
}
log.debug("stopped...c:{}",i);
}
}
结果
程序没有结束:
18:55:55.991 c.Test32 [Thread-0] - 把stop修改为true
18:55:56.100 c.Test32 [Thread-1] - true
解释
我们另外创建一个线程Thread1 在Thread0线程运行之后 Thread1线程在运行 打印此时stop的结果,结果发现此时结果为true,但是如果按照上面那个解释 修改的应该是副本 那么主存中的stop=false应该是不变的,Thread1也应该读取的是自己拷贝主存的数据stop=false,但现在结果为true,说明上述理论是错误的。
正确的解释:JMM(java内存模型)
解释:还是上面那张图,不过需要加一个条件 那就是每个副本在进行写操作后都会更新主存中的数据 更新后其它线程的副本也需要更新为主存中的数据 达到数据同步,但是其中会有一个问题就是 上述代码的问题 如果一个线程中有一个一直执行的代码块 就叫做热点代码块 jvm的jit就会对其进行优化 以后这个代码块中的不进行写操作的变量 就会直接从副本中拿 就算有其他线程更新了主存中的数据 这个线程对于这个变量依旧从副本中拿。
对于例子来说:由于t0多次读取while(!stop)导致jvm认为while这部分代码块是热点代码 然后jvm里jit编译器就做了优化 因为stop一直只有读操作 所以以后再读取直接读取高速缓存里的stop,但是其他代码的stop还用的还是最新主存拷贝的副本,所以才会出现,有一个线程更新了stop=true但是主线程就是获取不到 但是其他线程都可以获取到。
需要注意的一点是:只有处于热点代码块中的读操作的变量 才会一直从工作内存中获取,但是有写操作的变量 每次会更新到工作内存后还会更新到主存中,大家可以试试创建一个静态变量cnt=0 在while循环中cnt++,然后其他线程在睡眠几秒后持续读取cnt 会发现cnt一直在增加,也就说明cnt确实更新到主存中了
04.然后避免上述例子的问题
volatile的可见性
@Slf4j(topic = "c.Test32")
public class Test32 {
static volatile Boolean stop = false;
public static void main(String[] args) {
new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true;
log.debug("把stop修改为true");
}).start();
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("{}",stop);
}).start();
foo();
}
static void foo(){
int i=0;
while (!stop){
i++;
}
log.debug("stopped...c:{}",i);
}
}
结果
19:35:37.187 c.Test32 [main] - stopped…c:262126608
19:35:37.187 c.Test32 [Thread-0] - 把stop修改为true
19:35:37.279 c.Test32 [Thread-1] - true
解释
可以看出这次成功的使while退出了,原因是 volatile的可见性 会使线程每一次必须从主存中获取数据更新到工作内存中,所以每一次的数据都是最新,所以stop被其他线程更新后,这次 while(!stop)的stop就是true了
synchronized的可见性
@Slf4j(topic = "c.Test32")
public class Test32 {
static Boolean stop = false;
static String lock = "";
public static void main(String[] args) {
new Thread(()->{
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stop = true;
log.debug("把stop修改为true");
}).start();
new Thread(()->{
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.debug("{}",stop);
}).start();
foo();
}
static void foo(){
int i=0;
while (true){
synchronized (lock){
if (stop)
break;
}
i++;
}
log.debug("stopped...c:{}",i);
}
}
结果
19:47:45.452 c.Test32 [main] - stopped…c:5667729
19:47:45.452 c.Test32 [Thread-0] - 把stop修改为true
19:47:45.546 c.Test32 [Thread-1] - true
解释
可以看出我们利用synchronized也可以实现stop=true。
原因是:在Java内存模型中,synchronized规定,线程在加锁时, 先清空工作内存→在主存中拷贝最新变量的副本到工作内存 →执行完代码→将更改后的共享变量的值刷新到主内存中→释放互斥锁。
边栏推荐
- cuda_error_out_of_memory(out of memory怎么办)
- Programmer's Guide to health quenching 5: introduction to sports Basics
- Experience sharing of system architecture designers preparing for the exam: from point to surface
- Airtest解决“自动装包”过程中需要输入密码的问题(同适用于随机弹框处理)
- Vivo official website app full model UI adaptation scheme
- In June 2021, the interview suffered a Waterloo. Is it so convoluted now
- Niuke-top101-bm38
- Autojs learning - file depth search
- [ManageEngine] value brought by Siem to enterprises
- ES6 --- four powerful operators (?,? =,?.,?:)
猜你喜欢

How to choose sentinel vs. hystrix current limiting?

Zero basic learning canoe panel (17) -- panel CAPL function

Reading the pointpillar code of openpcdet -- Part 3: Calculation of loss function

Too many passwords, don't know how to record? Why don't you write a password box applet yourself

Huawei occupies half of the folding mobile phone market, proving its irreplaceable position in the high-end market

Matlab---eeglab check EEG signal
What's special about Huawei's innovative solutions to consolidate the foundation of ERP for small and medium-sized enterprises?

两数,三数之和

Miscellaneous notes -- a hodgepodge

Stm3 (cubeide) lighting experiment
随机推荐
Niuke-top101-bm38
Programmer's Guide to health quenching 5: introduction to sports Basics
[fiddlertx plug-in] use Fiddler to capture the package Tencent classroom video download (unable to capture the package solution)
五、品达通用权限系统__pd-tools-xxs(防跨站脚本攻击)
Debugged PEB (beingdebugged, ntglobalflag)
npm 模块 移除_【已解决】npm卸载模块后该模块并没有从package.json中去掉[通俗易懂]
The adequacy of source evaluation forum · observation model test
GDB locates the main address of the program after strip
[ManageEngine] value brought by Siem to enterprises
图片怎么存储到数据库里「建议收藏」
有哪些优化mysql索引的方式请举例(sqlserver索引优化)
ES6---4个强大运算符(??、??=、?.、?:)
Opencv learning Fourier transform experience and line direction Fourier transform code
Research: more than 70% of doctors are still prescribing unsafe antibiotic drugs
Explain in detail the principle of MySQL master-slave replication "suggestions collection"
Database SQL statement exercise "suggestions collection"
Add startup software items when the win system starts up
一道golang中关于map的并发读写的面试题
Apple estimates that iPhone will give up the Chinese market, and the Chinese industrial chain needs to consider living a hard life
An interview question combining defer and function in golang