java – 内存不一致与线程交错有何不同?
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了java – 内存不一致与线程交错有何不同?,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3887字,纯文字阅读大概需要6分钟。
内容图文
我正在编写一个多线程程序,正在研究是否应该使用volatile作为我的布尔标志.关于并发性的文档oracle跟踪没有解释除了以下内容之外的内存一致性错误:
Memory consistency errors occur when different threads have
inconsistent views of what should be the same data.
假设这些不一致的视图仅在“写入”操作之后发生是有意义的.但是多久之后呢?
例1
Thread A: Retrieve flag.
Thread B: Retrieve flag.
Thread A: Negate retrieved value; result is true.
Thread A: Store result in flag; flag is now true.
Thread B: System.out.print(flag) --> false
由于线程A和线程B同时运行,因此打印也可能导致为true,具体取决于检索标志的时间.对于不一致而言,这是完全合理的.
但是描述内存一致性错误的方式(写入变量不一定反映在其他线程中)听起来这也是如此:
例2
Thread A: Retrieve flag.
Thread A: Change retrieved value; result is true.
Thread A: Store result in flag; flag is now true.
//any longer amount of time passes (while Thread A didn't explicitly happen-before Thread B, it obviously did.)
Thread B: Retrieve flag.
Thread B: System.out.print(flag) --> true OR false (unpredictable)
我强烈认为示例2不可能是真的.问题是,只有当它是真的,我才能看到使用volatile建立一个先发生过的事情.
如果是真的,为什么会这样呢?如果不是..为什么要使用挥发性物质呢?
解决方法:
关于JVM内存模型最难理解的一点是,严格来说,时序(即你的挂钟)是完全无关紧要的.
无论多长时间(根据您的挂钟)在2个独立线程中的2个操作之间经过了多长时间,如果没有发生在之前的关系,则绝对不能保证每个线程在内存中看到的内容.
在您的示例2中,您提到的棘手部分是
while Thread A didn’t explicitly happen-before Thread B, it obviously did.
从上面的描述中,你可以说唯一明显的是,根据你的挂钟测量的时间,一些操作比其他操作发生得晚.但这并不意味着严格意义上的JVM内存模型之前发生过关系.
让我展示一组与您上面的示例2的描述兼容的操作(即,根据您的挂钟所做的测量),这可能导致真或假,并且不能保证.
>主线程M启动线程A和线程B:线程M和线程A之间以及线程M和线程B之间存在先发生关系.因此,如果没有其他事情发生,线程A和线程B都将看到与该布尔值的线程M相同的值.我们假设它被初始化为假(以使其与您的描述兼容).
假设您在多核计算机上运行.此外,假设线程A在Core 1中分配,而线程B在Core 2中分配.
>线程A读取布尔值:它必须读为false(参见前面的项目符号点).当这种读取发生时,可能会发生某些内存页面(包括包含该布尔值的内存页面)将被缓存到Core 1的L1缓存或L2缓存中 – 该特定内核的本地缓存.
>线程A否定并存储布尔值:它现在将存储为true.但问题是:在哪里?在发生之前发生之前,线程A可以自由地将此新值存储在运行该线程的Core的本地缓存中.因此,该值可能会在Core 1的L1 / L2高速缓存上更新,但在处理器的L3高速缓存或RAM中保持不变.
>经过一段时间后(根据你的挂钟),线程B读取布尔值:如果线程A没有将更改刷新到L3或RAM中,则线程B完全可能读取错误.另一方面,如果线程A刷新了更改,则线程B可能会读取为真(但仍然无法保证 – 线程B可能已收到线程M的内存视图副本,并且由于缺少发生之前,它不会再次进入RAM并仍然会看到原始值).
保证任何事情的唯一方法是在之前有一个明确的发生:它会强制线程A刷新其内存,并强制线程B不从本地缓存读取,而是真正从“权威”源读取它.
如果没有事先发生,正如您在上面的示例中所看到的那样,无论在不同线程中的事件之间经过了多长时间(从您的角度来看),任何事情都可能发生.
现在,一个大问题:为什么volatile会解决你的例2中的问题?
如果该布尔变量被标记为易失性,并且如果根据上面的示例2(即,从挂钟的角度)发生操作的交错,那么,只有这样,线程B才能保证看到真(即,否则存在根本没有保证.
原因是,挥发性有助于建立先发生过的关系.它如下所示:在对同一变量的任何后续读取之前发生对易失性变量的写入.
因此,通过标记变量volatile,如果从定时的角度看,线程B仅在线程A更新之后读取,则线程B保证看到更新(从内存一致性的角度来看).
现在有一个非常有趣的事实:如果线程A对非易失性变量进行更改,则更新volatile变量,然后(从挂钟角度来看)线程B读取该volatile变量,同时保证线程B将看到所有更改到非易失性变量!这是由非常复杂的代码使用,它们希望避免锁定并且仍然需要强大的内存一致性语义.它通常被称为volatile volatile捎带.
作为最后一点,如果你试图模拟(缺乏)发生在之前的关系,它可能会令人沮丧……当你把东西写到控制台(即System.out.println)时,JVM可能会做一个多个不同线程之间有很多同步,所以很多内存可能实际上都被刷新了,你不一定能看到你想要的效果……很难模拟这一切!
内容总结
以上是互联网集市为您收集整理的java – 内存不一致与线程交错有何不同?全部内容,希望文章能够帮你解决java – 内存不一致与线程交错有何不同?所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。