内存可见性??
举例:
public class ThreadDemo17 {
static class Counter{
public int flag = 0;
}
public static void main(String[] args) {
Counter counter = new Counter();
// 下面两个线程中涉及到的 counter 都是同一个对象
Thread t1 = new Thread(){
@Override
public void run(){
while (counter.flag == 0){
}
System.out.println("循环结束!!");
}
};
t1.start();
Thread t2 = new Thread(){
@Override
public void run(){
Scanner scan = new Scanner(System.in);
System.out.print("请输入一个整数: ");
// 在此处修改 flag,按理说,是会影响到 t1 中的 flag
counter.flag = scan.nextInt();
}
};
t2.start();
}
}
预期效果:
线程1,先进入循环;线程2,会读取用户输入的一个整数
随着用户输入了一个非0 的整数后,此时,线程1 的循环就会随之终止
实际效果:
线程2,输出数据完毕后,发现线程1 的循环并没有结束
这是为什么?????
这样的现象背后,涉及到了 编译器的优化
[ 注意 ], 编译器的优化,必须保证一个前提:优化后的逻辑和优化前是等价的
但是此处,编译器错误的优化,导致程序出现了 bug ( 这是编译器自身的"坑"~~ )
上述的优化策略,就是"内存可见性"
若优化生效,内存就是不可见的了 (其他线程修改了也看不见)
若优化不生效,内存才是可见的 (其他线程修改能看见)
因此 volatile 关键字的作用,也就是:禁止编译器进行上述场景的优化
(一个线程读,一个线程写,修改对于读线程来说可能没生效)
故,刚才的代码可以加上 volatile 关键字:
static class Counter{
public volatile int flag = 0;
}
此时的输出结果:
加上 volatile 关键字,手动禁止了这样的优化
在之前的 线程安全篇 里,我们举了一个例子
那么,我们如果加上关键字 volatile,会解决这里的线程不安全问题嘛??
public class ThreadDemo18 {
static class Counter{
// 此处加上 关键字 volatile
public volatile int count = 0;
public void increase(){
count++;
}
}
public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread t1 = new Thread(){
@Override
public void run(){
for (int i = 0; i < 50000; i++) {
counter.increase();
}
}
};
t1.start();
Thread t2 = new Thread(){
@Override
public void run(){
for (int i = 0; i < 50000; i++) {
counter.increase();
}
}
};
t2.start();
t1.join();
t2.join();
System.out.println(counter.count);
}
}
执行验证:
我们发现,volatile 关键字,并不能解决这个问题,最终 count 的值仍然无法保证是 100000
线程不安全的场景:
1.一个线程读,一个线程写 ( volatile 关键字解决)
2.两个线程写 (加锁解决线程安全问题)
下篇:通信 — 对象的等待集
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/m0_47988201/article/details/121535667
内容来源于网络,如有侵权,请联系作者删除!