当前位置: 首页 > 知识库问答 >
问题:

Java volatile是否阻止缓存或强制执行直写缓存?

董飞
2023-03-14

我试图理解Java的易失性关键字,关于在具有CPU缓存的多线程程序中写入易失性原子变量。

我已经阅读了一些教程和Java语言规范,特别是关于“订购前发生”的第17.4.5节。我的理解是,当一个线程将一个新值写入一个可变变量时,更新后的值必须对读取该变量的其他线程可见。对我来说,这些语义可以通过以下两种方式之一实现:

>

  • 线程可以在CPU缓存中缓存易失性变量,但对缓存中变量的写入必须立即刷新到主内存。换句话说,缓存是直写的。

    线程永远不能缓存易失性变量,必须在主存储器中读写这些变量。

    本教程中提到了方法1(http://tutorials.jenkov.com)也就是说:

    通过声明计数器变量为volatile,所有对计数器变量的写入都将立即写回主存。

    方法2在Stackoverflow问题“Java中的Volatile variable”中提到,本教程也提到:

    这个变量的值永远不会被本地线程缓存:所有读写操作都将直接进入“主内存”

    Java中使用的正确方法是哪一种?

    相关的Stackoverflow问题不回答我的问题:

    Java中的可变变量

    是否Java易失性读取刷新写入,以及易失性写入是否更新读取

    Java易失性与缓存一致性

  • 共有2个答案

    娄振
    2023-03-14

    在Java内,最准确的说法是,所有线程都将看到对易失性字段的最新写入,以及在该易失性读取/写入之前的任何写入。

    在Java抽象中,这在功能上等同于从共享内存读/写易失性字段(但在较低级别上,这并不严格准确)。

    与Java相关的级别要低得多;在现代硬件中,对任何和所有内存地址的任何和所有读/写总是首先发生在L1和寄存器中。话虽如此,Java的设计目的是对程序员隐藏这种低级行为,因此这只是概念上与讨论有关。

    当我们在Java的字段上使用易失性关键字时,这只是告诉编译器在读取/写入该字段时插入称为内存屏障的东西。记忆屏障有效地确保了两件事;

    >

  • 任何读取此地址的线程都将使用最新的值(屏障使它们等待,直到最近的写入将其返回到共享内存,并且在更新后的值到达一级缓存之前,任何读取线程都不能继续)。

    对任何字段的读/写都不能跨越障碍(也就是说,它们总是在另一个线程继续之前写回,编译器/OOO不能将它们移动到障碍之后的某个点)。

    举一个简单Java例子;

    //on one thread
    counter += 1; //normal int field
    flag = true; //flag is volatile
    
    //on another thread
    if (flag) foo(counter); //will see the incremented value
    

    本质上,当将标志设置为时,我们会创建一个内存屏障。当线程#2尝试读取此字段时,它会遇到我们的障碍,并等待新值到达。同时,CPU确保在新值到达之前回写计数器=1。因此,如果flag==true,那么计数器将增加。

    总而言之;

    >

  • 所有线程都会看到易失性字段的最新值(可以粗略地描述为“读/写通过共享内存”)。

    对易失性字段的读取/写入与一个线程上的任何字段的先前读取/写入建立发生前关系。

  • 衡丰茂
    2023-03-14

    这些保证只是您在语言规范中看到的。理论上,写入易失性变量可能会强制缓存刷新到主存储器,也可能不会,可能是随后的读取强制缓存刷新,或者以某种方式导致缓存之间的数据传输而没有缓存刷新。这种模糊性是故意的,因为它允许潜在的未来优化,如果更详细地说明波动变量的机制,这些优化可能是不可能的。

    实际上,在当前的硬件中,这可能意味着,如果没有一致的缓存,写入易失性变量将迫使缓存刷新到主内存。当然,有了一致的缓存,就不需要这样的刷新了。

     类似资料:
    • 问题内容: 因为我以前使用过jQuery的AjAX方法,所以我对XMLHttpRequests较新。但是,我需要在网络工作者中工作,现在我必须使用经典的XMLHttpRequest来解决性能问题。 我正在尝试从jquery 重建-property。如果应该禁用缓存,请添加以下内容: 但是,如果我想强制缓存(不是阻止),应该设置哪个标头? 问题答案: 您可以设置各种标头来鼓励缓存,但是它们(包括您使

    • 问题内容: 它可能是实现细节,但是对于Oracle和IBM JDK而言,至少是对已编译模式进行了缓存,还是作为应用程序开发人员我们需要自己对已编译模式进行缓存? 问题答案: 我不认为结果会被缓存,并且代码或文档中也没有这种行为的证据。自己实现这样的缓存(当然)是比较琐碎的,但是我对这样的缓存很有用的用例感兴趣。 回覆。下面的注释和String.split(),有一种不同的方法,即代码采用简单的1或

    • 是否可以配置Spring,以便在缓存未命中时,对可缓存方法的调用将被阻止,直到可缓存方法执行一次并填充缓存? 在我的例子中,我处理的是数据库中的数据,这些数据不会经常更改。事实上,如果这些数据更改,则需要重新启动应用程序。我可以创建方法,并在每个服务启动时初始化数据,但这似乎不像注释那样“优雅”。 我计划将EhCache与Spring注释一起使用。 更新: 以下是我在尝试使用@PostConstr

    • 问题内容: 如何在HTML5视频上强制中止事件?我有一个叠加层,当我关闭它时,视频应该暂停播放,然后 停止缓冲 。但是,我的互联网连接仍然发疯。哦,我在Mac OS X 10.6上使用Chrome 7.0.5。 我已经尝试了几件事-没有一个起作用: (对于那些不熟悉XUI的人,x $就像jQuery的包装函数一样) 首先,调度中止HTML事件: 接下来,更改src,然后强制加载: 编辑:我的视频元

    • 问题内容: 问题: 当我两次运行相同的go测试时,第二次运行根本没有完成。结果是第一次运行时的缓存结果。 链接 我已经检查过https://golang.org/cmd/go/#hdr- Testing_flags, 但是没有cli标志用于此目的。 题: 是否有可能强制执行测试以始终运行测试而不缓存测试结果。 问题答案: 测试标志docs中描述了一些选项: :使所有测试结果失效 在测试运行中使用不

    • 问题内容: 有什么办法可以在页面上放置一些代码,以便当有人访问网站时,它会清除浏览器缓存,以便他们可以查看更改? 使用的语言:ASP.NET,VB.NET,当然还有HTML,CSS和jQuery。 问题答案: 如果这是关于和变化,一种方法是到“缓存无效”是通过附加像“ ”为每个版本的文件名。例如: 或者,在文件名之后执行此操作: