Java内存模型深度揭秘:多线程并发真相难以可控

举报
赵KK日常技术记录 发表于 2023/11/27 16:22:15 2023/11/27
【摘要】 多年来,Java并发编程一直是一个让开发者头疼不已的问题。无论使用哪种编程模型,线程安全问题总会随时出现。而我们之所以难以掌握多线程并发的真相,很大一部分原因就是因为Java内存模型(JMM)的存在。JMM定义了Java线程如何访问共享变量,以及变量值的传播规则。这对我们理解线程安全至关重要。本文将带你深入剖析JMM的工作原理,揭开它给并发编程带来的影响。这对你理解并控制多线程程序的行为将很...

多年来,Java并发编程一直是一个让开发者头疼不已的问题。无论使用哪种编程模型,线程安全问题总会随时出现。而我们之所以难以掌握多线程并发的真相,很大一部分原因就是因为Java内存模型(JMM)的存在。

JMM定义了Java线程如何访问共享变量,以及变量值的传播规则。这对我们理解线程安全至关重要。本文将带你深入剖析JMM的工作原理,揭开它给并发编程带来的影响。这对你理解并控制多线程程序的行为将很有帮助。

首先,我们来回顾一下单线程程序中变量值的传播:

int x = 0;
x = 3; 
int y = x;

在单线程环境中,我们期望的结果是y的值为3。但在多线程环境中,情况就不一定了。

这是因为JVM允许重排序指令,以提高性能。例如,一个线程修改变量x的值,另一个线程可能首先读取变量y,而非x,导致看到的结果不一致。

JMM通过 Happen-Before 原则来约束指令重排序:

  • 程序顺序规则:一个线程内,按照程序顺序进行的操作,后面操作的结果对前面可见
  • 监视器锁规则:对一个共享变量的写与对这个变量的监视器锁的解锁具有Happens-Before关系
  • volatile变量规则:对一个volatile变量的写与后续读/写该变量的线程间形成Happens-Before关系
  • 线程开始规则:Thread对象的start()方法的调用与线程内首次访问特定变量的 Happens-Before关系
  • 线程中断规则:对线程interrupt()方法的调用与被中断线程检测interrupt状态的Happens-Before关系
  • 线程终止规则:线程中最后一个指令的完成与线程结束的Happens-Before关系

这些规则限制了指令重排序,保证了一个线程修改变量值后,其他线程可以看到最新值。

但是,规则也不能消除所有并发问题。一个典型案例:

int x = 0, y = 0;

thread1(){
  x = 1;
  y = 1;
}

thread2(){
  if(x==1) 
    System.out.println("x equals 1");
  
  if(y==1)
    System.out.println("y equals 1");
}

这里x=1和y=1语句没有同步控制,线程2打印结果顺序不确定。这时我们就需要同步控制来保证程序顺序:

synchronized(this){
  x = 1;
  y = 1;
}

所以,理解JMM的Happen-Before原则,并掌握如何使用同步控制来保证程序顺序,这对我们正确编写线程安全程序至关重要。

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。