线程上下文切换是如何完成的?
Posted
技术标签:
【中文标题】线程上下文切换是如何完成的?【英文标题】:How is thread context switching done? 【发布时间】:2011-06-01 22:40:32 【问题描述】:据我了解,在进程上下文切换操作系统“备份”寄存器和指令指针(也是寄存器的一部分)。
但是如果在进程中的线程之间切换,操作系统会备份完整的寄存器内存和堆栈吗?
我问这个的原因是要了解Java的volatile关键字在单核处理器的情况下是否有任何意义。
【问题讨论】:
【参考方案1】:如果 Java 的 volatile 关键字在单核处理器的情况下具有任何意义。
jit 编译器使用的优化可能会导致意外的行为。
static boolean foo = true;
public void bar()
while(foo)
//doSomething
//do not modify foo in here
这可能会被优化,因为 foo 在循环内没有改变。
public void bar()
while(true)
//Now this loop never ends
//changes to foo are ignored
制作 foo
volatile
将告诉 jit 编译器 foo
可以被不同的线程更改并且不应该优化对它的访问。
这是有效的行为,因为跨线程访问只能保证使用
易失和同步的关键字 状态为线程安全的类(例如 java.util.concurrent.*)更新
volatile 关键字本身不会影响上下文切换,但它会影响变量读取和写入的优化方式。这不仅会影响 cpu 缓存的使用(对多核系统很重要),还会影响及时编译器使用的优化,如上所示(对所有系统都很重要)。
【讨论】:
【参考方案2】:了解 Java 的 volatile 关键字在单核处理器的情况下是否有意义。
这种思路是不明智的。您应该按照 API 和虚拟机的(记录的)定义进行编程。您不应该依赖某些具有特定效果或缺乏效果的东西(在这种情况下,volatile
的效果)不属于其文档定义的一部分。 即使实验表明它在特定情况下具有特定行为。因为它会咬你。
【讨论】:
volatile 的行为有据可查(参见 JSR-133)。 定义明确。但我相信,它不是处理器数量。【参考方案3】:线程切换确实意味着存储所有计算寄存器和所有堆栈,因为每个线程确实有一个单独的内存堆栈。
由于 Java 内存模型的工作方式,volatile
关键字在多线程中仍然很重要,即使在单核环境中也是如此。 volatile
变量不存储在任何类型的缓存或寄存器中,而是始终从主内存中获取,以确保每个线程始终看到变量的最新值。
【讨论】:
【参考方案4】:是的,即使对于单核处理器,volatile
仍然有用。它告诉编译器每次读取该值时都需要从内存中重新读取(因为另一个线程可能正在更新它),而不仅仅是缓存在寄存器中。
【讨论】:
【参考方案5】:查看JSR 133,尤其是标有“volatile 做什么?”的部分
易变字段是特殊字段 用于通信状态 线程之间。每读一个 volatile 将看到最后一次写入 任何线程的易失性;在 效果,它们被指定 程序员作为它所在的领域 永远不能接受看到“陈旧” 由于缓存或 重新排序。
这是对 volatile
如何与 JVM 内存模型一起工作的有用介绍和描述。
【讨论】:
【参考方案6】:我知道volatile
做了什么,但我试图理解为什么这是必要的。让我详细说明。
正如上面 josefx 所提到的。如果我们有类似下面的内容:-
while(run)
//do something but don't modify run
如果其他线程将run
变为false
,则while 循环将永远不会结束。正如 Yuval A 之前提到的,在线程上下文切换期间,寄存器也被备份。所以我猜它是这样工作的。
如果任何线程修改run
的值,该值将仅在寄存器中修改。在线程上下文切换期间,如果没有明确标记为volatile
,Java 不会将此寄存器值与 RAM 中的副本同步。这样每个线程都可以使用自己的run
版本。但是这个概念不适用于非基元和整数等基元,它们不能完全放入寄存器中,迫使它们与 RAM 副本同步。
【讨论】:
以上是关于线程上下文切换是如何完成的?的主要内容,如果未能解决你的问题,请参考以下文章