Java内存模型:单线程和多核CPU

问题描述 投票:0回答:1

在Java应用程序中,如果对对象状态的访问发生在同一线程上(在最简单的情况下,在单线程应用程序中),则无需进行同步以强制更改的可见性/一致性,如之前所述关系规范:

“两个动作可以通过在发生之前的关系进行排序。如果一个动作在另一个发生之前,则第一个对第二个可见并且在第二个之前进行排序。

[如果我们有两个动作x和y,我们写hb(x,y)表示x发生在y之前。

如果x和y是同一线程的动作,并且x按程序顺序位于y之前,则hb(x,y)。“

但是现代体系结构是多核的,因此Java线程可以在任何给定的时间在它们中的任何一个上执行(除非这不是真的,并且Java线程固定在特定的内核上?)。因此,在这种情况下,如果线程写入变量x,将其缓存在L1缓存或CPU寄存器中,然后开始在先前访问过x并将其缓存在寄存器中的另一个内核上运行,则该值是不连续的。 。当从CPU上卸下线程时,是否有某种机制(隐式内存屏障)?

java concurrency synchronization memory-barriers memory-model
1个回答
0
投票

可以在任何给定时间对它们中的任何一个执行]

任务不只是自发地在内核之间迁移。这些事情必须发生:

  • 该任务在先前运行的内核上被抢占(将其标记为等待在内核的全局任务列表中运行)
  • 另一个内核上的内核的任务调度程序看到该任务正在等待CPU,并决定运行它。
  • (调度是一种分布式算法;每个内核都非常有效地在该内核上运行内核,就像多线程进程。一个内核无法告诉另一个内核该怎么做,只能将数据放入运行内核的内存中该核心可以查看它。)


这不是问题,因为:

数据高速缓存(L1等)在操作系统可以在其上调度线程的所有核心上都是一致的。

Myths Programmers Believe about CPU Caches

或者在一个假设的,不太可能的OS + JVM上,该线程在具有非一致性共享内存的内核之间运行线程,在将任务置于一个内核上之后,操作系统必须在某个时刻将脏的私有缓存刷新回实际的共享内存。它放在全局任务队列中,另一个内核上的任务调度程序可以在其中运行它。


仅寄存器是真正的私有寄存器,是的,编译器会将变量保留在寄存器中。我不喜欢这样的术语“缓存”;在asm中,寄存器与内存分开。编译器可以在循环期间将变量的[[only

当前有效副本保存在寄存器中,然后将其存储回去。

每个任务都有其自己的寄存器状态;

这称为“体系结构状态”,是上下文切换保存/恢复的状态。

重新开始在另一个内核上执行线程方法

从内存中恢复其保存的寄存器状态,最后恢复其程序计数器]]。即跳转到停止处的指令,将程序计数器恢复到体系结构程序计数器寄存器中。例如在x86-64上的RIP。 (“指令指针”寄存器的64位版本)
请注意,寄存器和(虚拟)内存内容是用户空间进程(以及与之关联的打开文件描述符和其他内核内容)的整个状态。但是缓存状态不是。寄存器不是缓存。

有时会把负载从循环中吊起并保存在寄存器中,这被描述为在寄存器中“缓存”一个值,但这只是个随意的术语,您在这里会感到困惑。编译器-开发人员的术语是变量的“注册”。

© www.soinside.com 2019 - 2024. All rights reserved.