【发布时间】:2016-03-20 15:20:03
【问题描述】:
我在使用多线程 Java 程序时遇到了一些问题,并将其提炼成一个非常简单的示例——我的困惑仍然不少!
下面显示的示例程序。那么这是做什么(或打算做什么)?嗯,main() 函数从一个简单的线程开始,它基于一个静态的内部类 Runnable。此 Runnable 包含两个嵌套循环,对局部变量“z”进行简单计算,总共 10^12 次迭代 (10^6 * 10^6),之后它将打印出结果并退出。在生成这个工作线程之后,主线程进入它自己的循环,在其中将字符串“Z”打印到控制台,之后它休眠(使用 Thread.sleep())1 秒钟,然后一遍又一遍地重复这个过程。
所以运行这个程序,我希望它在计算线程执行其工作时每 1 秒打印一次“Z”。
然而,实际上发生的是计算线程启动,主线程显示第一个“Z”,然后什么也没发生。它似乎无限期地或至少比请求的 1000 毫秒长得多,挂在 Thread.sleep 调用中。
请注意,这是在具有多线程的快速四核机器上,因此同时运行线程应该没有问题。其他核心在 Windows 任务管理器中显示为空闲。此外,即使在单核系统上,我也希望操作系统定期抢占计算线程,以允许主线程打印字符串。 此外,线程之间没有共享变量或锁,因此它们不应相互阻塞。
更奇怪的是,Runnable 中有两个嵌套循环似乎对行为至关重要。只需一个循环,使用相同的迭代次数,一切都会按预期进行。
我已经在 Windows 10 64 位和 Java 1.8.0_73 上对此进行了测试。
有人可以解释这种行为吗?
public class Calculate {
static class Calc implements Runnable
{
@Override
public void run() {
int z = 1;
for(int i = 0; i < 1000000; i++) {
for(int j = 0; j < 1000000; j++) {
z = 3*z + 1;
}
}
System.out.println("Result: " + z);
}
}
public static void main(String[] args) throws Exception
{
Thread t = new Thread(new Calc());
t.start();
while(true) {
System.out.println("Z");
Thread.sleep(1000);
}
}
}
更新 1:有人建议这可能与 Busy loop in other thread delays EDT processing 重复。在我的案例中,许多症状是相同的,但很难判断它们是否真的是相同的原因,因为另一个问题似乎没有被完全诊断出来。但这很有可能。
更新 2:我已向 Oracle 提交了错误报告。如果它被接受并且我找到它,我会发布链接。
更新 3:在 Java 8 和 9 中被接受为错误:https://bugs.openjdk.java.net/browse/JDK-8152267
【问题讨论】:
-
这里似乎可以正常工作,您可能需要在
Calc的最内层循环中添加Thread.yield。此外,您的z最终会溢出,并且您正在循环进行 非常 大量迭代。 -
@Elliott:有趣的是,它按预期工作。为什么 yield() 是必要的,我的意思是有很多程序在后台线程中进行“深度”计算,通常不建议它们必须屈服,而且我实际上从未见过在 Java 中这样做过。确实“z”会溢出,但此计算仅用于测试示例进行一些虚拟计算。对于 int 来说,溢出是完全合法且明确定义的。并且故意增加迭代次数来展示问题。但请注意,CPU 密集型计算需要很长时间是很常见的。
-
我想我正在寻找的是某种关于“这个程序在后台计算的方式有什么问题”的指南。 IE。如果这是错误的,那么背景计算的其他情况可能是错误的。什么时候你必须让步等等的规则是什么?
-
您正在测试的机器上有多少个内核?您正在运行多少其他进程?
Thread适合我。 -
@Elliott:这是一个 6700K - 4 个真正的核心(4 GHz)和 8 个逻辑核心,由于超线程。没有其他重要进程正在运行(标准 Windows 和 Eclipse 除外)。当我启动程序时,CPU 使用率为 1%,并且似乎没有任何内核在忙于任何事情。也没有其他系统问题。你用的是哪个操作系统和Java版本?
标签: java multithreading