Java | 多线程(内存可见性)

问题引入

代码

目前有如下两个线程,根据代码逻辑可以得知,当 t2 将 isQuit修改为 true 后,t1 中的 while 应该立刻结束,随后 t1 线程退出。

public class Main {  
    static boolean isQuit = false;  
    public static void main(String[] args) throws InterruptedException {  
        Thread t1 = new Thread(()->{  
            try {  
                Thread.sleep(2000);  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }            while(isQuit){  
              
            }  
            System.out.println("t1线程结束");
        });        
        Thread t2 = new Thread(()->{  
            try {  
                Thread.sleep(3000);  
            } catch (InterruptedException e) {  
                throw new RuntimeException(e);  
            }            
            isQuit = true; 
             
        });        
        t1.start();  
        t2.start();  
        t1.join();  
        t2.join();  
    }}

分析

然而实际的运行结果却并没有输出"t1线程结束",这是为何?
首先初步分析,在 Java 的程序中有“两个内存”,一个是主内存,一个是工作内存,isQuit 本身是存储在主内存中的,而编译器为了加快代码的执行速度,将 isQuit 放入了工作内存;接下来 t2 线程修改了主内存中的 isQuit 。然而不会影响到工作内存中的 isQuit,因为编译器优化的问题,t1 不会再去主内存中读取 isQuit 的值,所以 t1 线程一直不会停止。
注:

  • work memory:“工作内存”不是我们平常说的“内存”,它包含了CPU的寄存器和缓存
  • main memory: “主内存”才是平常说的“内存”
  • 造成这样的原因,是因为 Java 本身是跨平台语言,不同计算机不同操作系统内的组成也不同。因此对“内存”的命名方式也与我们传统认知不同。

原因

实际上这是由编译器进行代码优化造成的 bug,编译器本身是“好心的”,通过优化可以进一步提高代码的执行效率,优化能够在保持代码逻辑不变的情况下,调整代码生成的内容。如果代码是单线程的,那么往往不会出现什么问题,但是如果代码是多线程的,优化后可能会出现问题,优化后的逻辑和优化前的逻辑可能就不一样了。

如图所示,t1 线程一直在运行并没有退出,因此“t1线程结束”也没有输出。
在这里插入图片描述

在这种情况下,我们可以自己添加代码,来防止编译器对代码进行优化,保证内存的可见性

保证内存可见性

volatile 关键字

通过在代码中添加 volatile 可以保证内存可见性,这样编译器就不会进行优化了

static volatile boolean isQuit = false;  

对变量添加 volatile,其能够告诉编译器,不要对此处进行优化,因而 t1 线程就能够正常退出了。
如图所示,线程正常结束。
![[博客/Java/Pasted image 20240702130219.png]]

synchronized 关键字

之前我们提到过,synchronized能够保证线程安全(原子性),但是其也能够保证内存可见性,因此也可以通过为代码块“上锁”的方式来保证内存可见性。
补充:虽然 synchronized 和 volatile 都能保证内存可见性,但是volatile不能保证原子性

局限性

优化后无论是性能、执行效率上都会比优化前快很多,这也是编译器冒着这么大的风险进行优化的原因。因此也不要滥用 synchronized 和 volatile,因为滥用会造成代码的执行效率下降,系统的并发程度下降。编译器冒着这么大的风险进行优化,也是有原因的,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值