使用new Date().getTime() 给出挂钟时间。但这确实不是我们作为开发人员想要的(大多数时候,直到进行一些使用企业级基准测试的基准测试),因为挂钟时间受到许多后台进程的影响,所以要解决这个问题Java 提供了更复杂的 API 来测量时间。
要排除其他系统活动的影响,您需要改为测量应用程序“用户时间”。
- “用户时间”是运行应用程序自己的代码所花费的时间。
- “CPU 时间”是用户时间加上系统时间。这是您的应用程序使用 CPU 所花费的总时间。
以下示例演示使用ManagementFactory.getThreadMXBean() API 计算 CPU 和用户时间。
Thread thread = new Thread(){
public void run() {
for (int i = 0; i < 100000; ++i) {
int result = 0;
for (int j = 0; j < i + 1; ++j) {
result += j;
}
}
System.out.println("FOR approach: ThreadCpuTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()/1000000000d);
System.out.println("FOR approach: UserTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadUserTime()/1000000000d);
};
};
thread.start();
Thread thread2 = new Thread(){
public void run() {
for (int i = 0; i < 100000; ++i) {
int result = 0;
int j = 0;
while (j < i + 1) {
result += j++;
}
}
System.out.println("WHILE approach: ThreadCpuTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadCpuTime()/1000000000d);
System.out.println("WHILE approach: UserTime = " + ManagementFactory.getThreadMXBean().getCurrentThreadUserTime()/1000000000d);
};
};
thread2.start();
话虽如此,我真的不确定您为什么会出现意外行为,我在 Eclipse 和 IntelliJ IDE 中都运行了您的代码,而且我的 FOR 循环方法总是比 WHILE 循环快。
可能尝试重新启动 Eclipse 并运行较少数量的后台进程,或者不运行 Eclipse 但从 Java 命令行运行测试,以便确定结果。
从下面字节码分析可以看出,WHILE 和 FOR 循环的方法产生了相同的字节码,这意味着会有相同的汇编代码,因此 CPU 执行指令所需的时间相同。
但实际上,当我们在您的 IDE 或其他方式中运行时,会受到后台进程的影响,因此会观察到不同的时间。 但在这种特殊情况下 - WHILE v/s FOR,进行字节码分析并得出 WHILE 和 FOR 循环方法将花费相同时间的结论更为合适。
FOR循环字节码:
{
public Test2();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 21
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: iinc 1, 1
18: goto 2
21: return
LineNumberTable:
line 3: 0
line 4: 8
line 3: 15
line 6: 21
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
frame_type = 250 /* chop */
offset_delta = 18
}
WHILE循环的字节码:
{
public Test();
flags: ACC_PUBLIC
Code:
stack=1, locals=1, args_size=1
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
LineNumberTable:
line 1: 0
public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_0
1: istore_1
2: iload_1
3: bipush 10
5: if_icmpge 21
8: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
11: iload_1
12: invokevirtual #3 // Method java/io/PrintStream.println:(I)V
15: iinc 1, 1
18: goto 2
21: return
LineNumberTable:
line 3: 0
line 4: 2
line 5: 8
line 6: 15
line 8: 21
StackMapTable: number_of_entries = 2
frame_type = 252 /* append */
offset_delta = 2
locals = [ int ]
frame_type = 18 /* same */
}
延伸阅读: