Java 线程与操作系统线程
Posted
技术标签:
【中文标题】Java 线程与操作系统线程【英文标题】:Java Threads vs OS Threads 【发布时间】:2011-05-24 13:35:38 【问题描述】:看起来我搞砸了 Java 线程/操作系统线程和解释语言。
在开始之前,我确实了解绿色线程是 Java 线程,其中线程由 JVM 负责,整个 Java 进程仅作为单个 OS 线程运行。因此在多处理器系统上它是无用的。
现在我的问题是。我有两个线程 A 和 B。每个线程都有 10 万行独立代码。我在多处理器系统上的 Java 程序中运行这些线程。每个线程都将被赋予一个本机操作系统线程来运行,它可以在不同的 CPU 上运行,但是由于 Java 被解释,这些线程将需要一次又一次地与 JVM 交互以将字节码转换为机器指令?我对吗 ?如果是,那么对于较小的程序,Java 线程不会是一个很大的优势?
一旦 Hotspot 编译了这两个执行路径,它们都可以和原生线程一样好?我说的对吗?
[编辑]:另一个问题是,假设您有一个 Java 线程,其代码未经过 JIT 编译,您创建该线程并 start() 吗?操作系统线程和 JVM 如何交互来运行该字节码?
谢谢
【问题讨论】:
【参考方案1】:每个线程都会被赋予一个原生操作系统 线程到 RUN 可以运行在 不同的 CPU,但由于 Java 是 解释这些线程将需要 再次与 JVM 交互 再次将字节码转换为 机器指令 ?我说的对吗?
你混合了两种不同的东西;由 VM 完成的 JIT 和 VM 提供的线程支持。在内心深处,你所做的一切都会转化为某种本机代码。使用线程的字节码指令与访问线程的 JIT 代码没有什么不同。
如果是,那么对于较小的程序 Java 线程不会是一个很大的优势?
在这里定义小。对于短暂的进程,是的,线程不会产生太大的影响,因为您的顺序执行速度足够快。请注意,这又取决于要解决的问题。对于 UI 工具包,无论应用程序多么小,都需要某种线程/异步执行来保持 UI 响应。
当您拥有可以并行运行的东西时,线程也很有意义。一个典型的例子是在线程中进行大量 IO 并在另一个中进行计算。你真的不想仅仅因为你的主线程被阻塞做 IO 而阻塞你的处理。
一旦 Hotspot 编译了这两个 执行路径都可以和 本机线程?我说的对吗?
请参阅我的第一点。
线程确实不是灵丹妙药,尤其是当涉及到“使用线程使代码运行得更快”这一常见误解时。一点阅读和经验将是你最好的选择。我可以推荐一份this awesome book 的副本吗? :-)
@Sanjay:事实上,我现在可以重新构建我的 题。如果我有一个线程 代码尚未经过 JIT 处理 操作系统线程执行它?
我再说一遍,线程是与 JIT 完全不同的概念。让我们试着简单地看一下程序的执行:
java pkg.MyClass -> VM 定位方法 要运行 -> 开始执行 逐行方法的字节码-> 将每个字节码指令转换为 它的原生对应物 -> 指令 由操作系统执行 -> 执行的指令 通过机器
当 JIT 启动时:
java pkg.MyClass -> VM 定位方法 运行 已经过 JIT 的 -> 找到相关的 native 代码 对于那个方法->指令 由操作系统执行 -> 执行的指令 通过机器
如您所见,无论您遵循何种路线,VM 指令都必须在某个时间点映射到其本地对应项。是否存储该本机代码以供进一步重用或在其他情况下丢弃(优化,记得吗?)。
因此回答您的问题,每当您编写线程代码时,它 被翻译为本机代码并由操作系统运行。翻译是即时完成还是在那个时间点查找是完全不同的问题。
【讨论】:
抱歉咆哮,这个答案比预期的要长得多。 ;-) @Sanjay:如果我做对了,JIT 甚至会在线程第一次执行之前完成?本质上,JVM 首先为两个独立线程执行 JIT,然后转发到 OS 线程执行? @Geek:如前所述,JIT 和线程执行是两个正交的问题。是的,线程即将执行的一段代码很可能已经被虚拟机进行了 JIT 处理。此外,没有执行的转发。当在 Thread 对象上调用start()
方法时,会创建一个新的本机 OS 线程,“Java 线程”和“OS 线程”之间没有转发。此外,JIT 不是强制性的。您的线程当前可能正在执行未经过 JIT 处理的代码。 JIT 是一种针对性能的优化。
@Sanjay:就像您提到的“您的线程当前可能正在执行未经过 JIT 处理的代码。”那么如果是这种情况,操作系统线程如何解释字节码?它必须再次进入 JVM 吗?因此,如果线程 A、B 都必须这样做,这怎么可能是远程线程化的?它更像是顺序的。我错过了什么吗?
@Sanjay:事实上,现在我可以重新提出我的问题了。如果我有一个代码未经过 JIT 处理的线程,操作系统线程如何执行它?【参考方案2】:
整个 Java 进程仅作为单个 OS 线程运行
这不是真的。因此没有具体说明,我们经常看到,Java 线程实际上是本地 OS 线程,而多线程 Java 应用程序确实使用了多核处理器或多处理器平台.
一个常见的建议是使用线程池,其中线程数与内核数成正比(因子 1-1.5)。这是另一个提示,JVM 不限于单个 OS 线程/进程。
来自***:
在 Java 1.1 中,绿色线程是 JVM 使用的唯一线程模型,[4] 至少在 Solaris 上是这样。由于与原生线程相比,绿色线程有一些限制,随后的 Java 版本放弃了它们,转而支持原生线程。
现在,回到 2010 年,Java 7 正在开发中,Java 8 正在计划中 - 我们真的对历史悠久的“绿色线程”感兴趣吗??
【讨论】:
我只讨论了 JDK i.2 之前的特定线程,称为绿色线程。 @Geek - 你“提到”了绿色线程,但你的问题是(看标题)关于 Java 线程的一般性问题。如果您只对绿色线程和 Java 1.1/1.2 感兴趣,请编辑您的问题 当然。实际上,在绿色线程的情况下,很容易理解 JVM 在运行两个线程时如何处理字节码转换,因为 JVM 拥有整个控制权。如果两个线程作为操作系统线程运行,我无法掌握它。 @Andreas_D - Java 7 有什么变化?谢谢,只有当您有时间回复时。【参考方案3】:-
某些 Java 实现可能会创建
像你这样的绿色线程描述它
(JVM在一个
单个本机线程),但正常
在 PC 上使用 Java 的实现
多核。
JVM 本身可能已经使用不同的线程来完成工作(垃圾收集、类加载、字节码验证、JIT 编译器)。
操作系统运行一个名为 JVM 的程序。 JVM 执行 Java 字节码。如果每个 Java 线程都有一个关联的本机线程(这是有道理的,并且似乎在 PC 实现上就是这种情况),那么该线程中的 JVM 代码执行 Java 代码 - JITed 或解释 - 就像在单线程上一样-程序。多线程在这里没有区别。
【讨论】:
【参考方案4】:线程和运行字节码是不同的问题。 JVM 在没有原生线程支持的平台上使用绿色线程。 (恕我直言,我不知道哪个平台不支持线程)。
字节码由JVM实时解释并在本机平台上执行。 JVM 决定哪些是最流行的代码片段并执行所谓的即时编译这些片段,因此它不必一次又一次地编译它们。这与线程无关。例如,如果您有一个线程在循环中执行相同的代码片段,则该片段将被即时编译器缓存。
底线:不要担心性能和线程。 Java 足够强大,可以运行您正在编码的所有内容。
【讨论】:
Solaris 上的 Java 1.0 使用绿色线程。不确定从那以后是否有人这样做,但在设计中是允许的。 所以就像你说的,如果我通过 JIT 或通过热点拥有所有代码,我可以有效地将两个 Java 线程作为两个 OS 线程运行,这很公平。但我认为 JIT 编译发生在代码的第一遍,所以当我第一次运行启动两个线程的代码时,这两个线程仍然需要被解释吗?因此,两个操作系统线程都将涉及 JVM。 据我所知,JVM 在本机线程上创建了一个瘦包装器。一个应用程序中的所有线程共享相同的内存,因此编译为本机代码的代码将在线程之间共享。 非常接近“编译为本机的代码..”所有线程相关的 RUN 函数何时会发生这种情况?它甚至在调用 RUN 方法之前发生吗?如果是这样的话,我想我理解了这个谜题:-) 我还是没完全明白。如果 java 线程 A 映射到 OS 线程 B 并且 B 被安排在 CPU 上运行,那么 B 如何从 A 获取本机代码? JVM 是否编译字节码并将本机代码放置在一个公共位置,B 从那里不断读取本机代码并知道每当 B 获得 CPU 时间时接下来需要执行哪个语句?以上是关于Java 线程与操作系统线程的主要内容,如果未能解决你的问题,请参考以下文章