我已经删除了旧问题,因为这里的问题更清楚。如果在多核 CPU 上启动任何 java 进程,那么这个 java 进程将分配给哪个内核?这个操作系统是特定的吗?如果 java 进程启动了许多线程,那么其他内核是否有机会运行这些线程,或者只有启动 java 进程的内核才能处理这些线程?
寻找建设性的答案。
我已经删除了旧问题,因为这里的问题更清楚。如果在多核 CPU 上启动任何 java 进程,那么这个 java 进程将分配给哪个内核?这个操作系统是特定的吗?如果 java 进程启动了许多线程,那么其他内核是否有机会运行这些线程,或者只有启动 java 进程的内核才能处理这些线程?
寻找建设性的答案。
如果任何操作系统是多处理器的,那么 JVM 使用它们全部还是其中之一?
JVM 将使用操作系统允许它使用的尽可能多的内核......最多可运行线程的数量。
我问这个是因为在一个处理器中,一次只能运行一个线程,所以如果有 4 个处理器,那么当时会运行 4 个线程?
最多可以同时运行 4 个线程。这取决于线程是否可运行,以及操作系统是否准备好将应用程序的线程调度到 4 个内核上。(系统通常运行其他进程以及 JVM。)
并且由于处理器具有不同的内存上下文,因此它们相应的内存线程也将具有不同的内存上下文,因此它们不会出现并发问题?
Java 中的并发性是一个很大的话题。我建议您首先阅读有关并发主题的 Java 教程部分。
是的......不同的内核将共享相同的地址空间,您是正确的,它们将具有不同的“内存上下文”。具体来说,它们不会共享寄存器集,并且它们可能不会共享内存 1st / 2nd / 3rd 级内存缓存。但是,如果您遵循编写正确的多线程 Java 代码的规则,您应该不会遇到问题。这些规则(在 JLS 中指定为 Java 内存模型)清楚地说明了多线程程序可以和不能假设一个线程所做的更改何时对另一个线程可见。
更新
这是我的主要疑问:如果 JVM 正在使用 4 个进程(P1、P2、P3、P4),那么在一个进程下运行的线程将面临并发问题,P1 下的线程 T1 和 P2 下的线程 T2 永远不会有并发问题,因为它们具有不同的内存上下文。我是对还是错?
真的很难知道你的意思,因此很难判断你是“对”还是“错”。
然而:
如果您有 4 个进程,那么每个进程将运行自己的 JVM。每个 JVM 有一个进程。不多也不少。
每个线程属于一个 JVM(因此属于一个进程),并且在正常情况下不能1干扰属于另一个 JVM 的线程。不同的进程不会共享内存上下文,除非您采取措施使其发生。操作系统确保进程地址空间/内存上下文是分开的。(如果不这样做,那将是一个重大的安全问题!!)
属于一个 JVM 的线程可能会相互干扰。它们确实共享相同的虚拟地址空间,尽管内存共享并不完美(见上文关于缓存等)。
线程到内核的映射是动态的;即它可以改变毫秒。但是,操作系统会确保不同进程中的线程之间不会出现不必要的内存上下文泄漏。
1 - 当进程使用“共享内存”段时,情况很复杂。但是,您不能在纯 Java 中执行此操作,甚至在 JVM 内的本机代码库中执行此操作也将是“困难的”。
如果任何操作系统是多处理器的,那么 JVM 使用它们全部还是其中之一?
在大多数现代硬件架构上,JVM 将利用操作系统为其提供的所有 CPU/内核。
并且由于处理器具有不同的内存上下文,因此它们相应的内存线程也将具有不同的内存上下文,因此它们不会出现并发问题?
呃,不是吗?我不确定你在这里问什么,但并发的整个问题是 CPU 有自己的高速缓存。这就是内存同步和锁定如此重要的原因。一个线程可能对其缓存内存进行本地更改,或者由于其他线程对中央存储所做的更改,它的缓存内存可能会失效。因此,多处理器和不同的内存“上下文”肯定存在“并发问题”。
编辑:
这是我的主要疑问:如果 JVM 正在使用 4 个进程(P1、P2、P3、P4),那么在一个进程下运行的线程将面临并发问题,P1 下的线程 T1 和 P2 下的线程 T2 永远不会有并发问题,因为它们具有不同的内存上下文。我是对还是错?
你错了。JVM 将在 4 个处理器上调度您的 4 个用户线程和其他后台线程(gc、finalizer、jmx 等),与在同一操作系统上运行的其他进程竞争。在某个时间点,JVM 可能正在使用所有 4 个处理器,或者可能不使用任何处理器,具体取决于操作系统上运行的其他进程。
如果 T1 和 T2 在不同的处理器上,那么它们可以同时运行。除非它们共享数据,否则所有线程都不会出现并发问题。如果 T1 对 T2 也在使用的对象进行更改,则 T2 可能会或可能不会看到这些更改,具体取决于正在使用的同步。这种内存同步是多线程编程的大问题之一。例如:
static Integer sharedValue = 1;
...
// thread T1 changes the shared value
sharedValue = 2;
...
// thread T2 reads the shared value later
// it is unknown whether this will print 1 or 2
System.out.println("shared = " + sharedValue);
如果 T2 被换出而 T3 在同一个处理器上运行,实际上可能会减少并发问题,因为它们都使用相同的 CPU 缓存,但您仍然需要担心锁定问题。例如,如果 T2 任务执行一个++
看似原子但实际上是 3 个操作(get、increment、store)的任务,它可能会在增量之后但在存储之前被中断,并可能覆盖 T3 的增量:
static int counter = 0;
...
// thread T2
// 3 operations: get, increment, store
// this thread might be interrupted in the middle causing a ++ to be lost
counter++;
...
// thread T3
// 3 operations: get, increment, store
// this thread might be interrupted in the middle causing a ++ to be lost
counter++;
...
// later on, even if T2 and T3 have completed, you may see the counter as 1 or 2
System.out.println("counter = " + counter);
因此,即使您的线程是在多个或单个处理器系统上运行,锁定也是一个问题。有关非原子操作的更好示例,请参见此处。
听起来您需要了解线程和并行性之间的区别。
线程是一种执行原语,其中指令在 CPU 上运行
并行性是多个线程同时执行的可能性
作为实现细节,JVM 为内部目的运行多个线程。例如,垃圾收集器在与您的代码不同的线程上运行。同样,您使用的代码和库可能会生成任意数量的线程作为它们自己实现的一部分。它们是否会并行运行是另一回事。
操作系统负责将 CPU 内核分配给线程,以便它们可以运行,而这些最终会限制程序的潜在并行性。
是的,并发问题确实存在,因为线程本质上是邪恶的。