千家信息网

Java并发编程之volatile与JMM多线程内存模型实例分析

发表于:2025-01-16 作者:千家信息网编辑
千家信息网最后更新 2025年01月16日,本篇内容主要讲解"Java并发编程之volatile与JMM多线程内存模型实例分析",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Java并发编程之vola
千家信息网最后更新 2025年01月16日Java并发编程之volatile与JMM多线程内存模型实例分析

本篇内容主要讲解"Java并发编程之volatile与JMM多线程内存模型实例分析",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"Java并发编程之volatile与JMM多线程内存模型实例分析"吧!

一、通过程序看现象

在开始为大家讲解Java 多线程缓存模型之前,我们先看下面的这一段代码。这段代码的逻辑很简单:主线程启动了两个子线程,一个线程1、一个线程2。线程1先执行,sleep睡眠2秒钟之后线程2执行。两个线程使用到了一个共享变量shareFlag,初始值为false。如果shareFlag一直等于false,线程1将一直处于死循环状态,所以我们在线程2中将shareFlag设置为true

public class VolatileTest {  public static boolean shareFlag = false;  public static void main(String[] args) throws InterruptedException {    new Thread(() -> {      System.out.print("开始执行线程1 =>");      while (!shareFlag){  //shareFlag = false则一直死循环        //System.out.println("shareFlag=" + shareFlag);      }      System.out.print("线程1执行完成 =>");    }).start();    Thread.sleep(2000);    new Thread(() -> {      System.out.print("开始执行线程2 =>");      shareFlag = true;      System.out.print("线程2执行完成 =>");    }).start();  }}

如果你没有学过JMM线程模型,可能你看完上面的代码,希望得到的输出结果是下面这样的:

开始执行线程1 =>开始执行线程2 =>线程2执行完成 =>线程1执行完成=>

如下图所示,正常人理解这段代码,首先执行线程1进入循环,线程2修改shareFlag=true,线程1跳出循环。所以跳出循环的线程1会打印"线程1执行完成=>",但是经过笔者实验,**"线程1执行完成=>"不会被打印,线程1也没有跳出死循环**,这是为什么呢?

二、为什么会产生这种现象(JMM模型)?

要解释上面提到的问题,我们就需要学习JMM(Java Memory Model)Java 内存模型,笔者觉得叫做Java多线程内存模型更准确一些。

  • 首先,在JMM中每个线程有自己的工作内存,在程序启动的时候,线程将共享变量加载(read&load)到自己的工作内存中,加载到线程工作内存中的内存变量是主内存中共享变量的副本。也就是说此时shareFlag在内存中有三个副本,值都等于false。

  • 当线程2执行shareFlag=true的时候将其工作内存副本修改为shareFlag=true,同时将副本的值同步写回(store&write)到主内存中。

  • 但是线程1的工作内存中的shareFlag=false没有发生变化,所以线程1一直处于死循环之中

三、MESI 缓存一致性协议

按照上文的实验以及JMM模型,线程2修改的共享变量的值,线程1感知不到。那怎么样才能让线程1感知到共享变量的值发生了变化呢?其实也很简单,给shareFlag共享变量加上volatile关键字就可以了。

public volatile static boolean shareFlag = false;

其底层原理是这样的,加上volatile关键字提示JMM遵循MESI 缓存一致性协议,该协议包含如下的缓存使用规范(看不懂可以不看,下文会用简单的语言及例子描述一下)。

  1. Modified:代表当前Cache行的数据是修改过的(Dirty),并且只在当前CPU的Cache中是修改过的;此时该Cache行的数据与其他Cache中的数据不同,与内存中该行的数据也不同。

  2. Exclusive:代表当前Cache行的数据是有效数据,其他CPU的Cache中没有这行数据;并且当前Cache行数据与内存中的数据相同。

  3. Shared:代表多个CPU的Cache中都会缓存有这行数据,并且Cache中的数据与内存中的数据一致;

  4. Invalid:表示当前Cache行中的数据无效;

上文中的缓存使用规范可能过于复杂,简单的说就是

  • 当线程2修改shareFlag的时候(参考Modify),告知bus总线我修改了共享变量shareFlag,

  • 线程1对Bus总线进行监听,当它获知共享变量shareFlag发生了修改就会将自己工作内存中的shareFlag副本删除使其失效。

  • 当线程1再次需要使用到shareFlag的时候,发现工作内存中没有shareFlag变量副本,就会重新从主内存中加载(read&load)

到此,相信大家对"Java并发编程之volatile与JMM多线程内存模型实例分析"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

线程 内存 数据 变量 模型 工作 循环 副本 缓存 代码 时候 实例 实例分析 分析 编程 一致 代表 学习 不同 一致性 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 延庆区综合网络技术服务品质保障 idea数据库表格怎么加数据 北京永松网络技术有限公司 河南系统定制软件开发 软件开发信息安全技术服务 串口服务器接入设备数量过多 网络安全就业率多少 天津厉害的软件开发公司 番禺服务器运维服务哪里有 软件开发是什么学位呢 认证服务器连接超时 赤峰市正规软件开发培训学校 医院联网数据库 通信网络安全管理员中级理论考试 增强全社会网络安全 天津什么是软件开发过程检测中心 激战2哪个服务器人多 手机里的网络安全管理 广州市良策网络技术 vb 附加sql数据库 零号电大数据库应用技术 在数据服务器上建联合主键 数据库菜鸟驿站 linux可以配置什么服务器 1000人在线的视频服务器费用 泰国什么时候颁布网络安全法 互联网科技公司发展愿景 网络安全审计测评标准 视频加速服务器下载 山西指纹数据库
0