commented by Maurice Perry岁时,你设计了一个需要10秒执行的任务,但这是时钟上的10秒时间,而不是CPU活动的10秒.
您重复循环,判断每个循环的当前时间.你可以拨打System.currentTimeMillis()
查时间.您的意图是让CPU核心在整个10秒内都处于忙碌状态.但事实并非如此.
在Java中管理平台线程的主机OS根据自己 Select 的时间和持续时间调度一个线程执行.您的Java平台线程可能会随时被该主机操作系统暂停.因此,在一个极端的例子中,您的代码可能只判断当前时间一次,结果是2024-01-23T00:00:22.123Z.然后,主机操作系统可能会挂起您的线程.您的线程在几秒钟内什么都不做.最终,主机操作系统会调度该线程以供进一步执行.代码第二次判断时间时,结果是2024-01-23T00:00:34.567Z.因此,超过12秒过go 了,但您的代码只短暂地运行了两次,只是判断了两次当前时间.
这个例子有点极端,因为主机操作系统通常不会让线程在不执行的情况下运行12秒.但是,如果您的主机的CPU严重过载,例如运行1000个Java线程,则确实可能发生如此长的线程挂起.
因此,您所体验到的行为是一种功能,而不是错误.在系统时钟上等待10秒到期的1000个任务实际上总共需要大约10秒.
在我的MacBook Pro、16英寸、2021、Apple M1 Pro、10核(8性能和2效率)、16 GB RAM、MacOS Sonoma 14.3.1、IntelliJ IntelliJ Idea 2023.3.4(旗舰版)中的Java 21.0.1上运行您的准确代码需要20秒.
然后我修改了你的代码.我显示了在每个任务中执行的循环的数量.
package work.basil.example.threading;
import java.time.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class BusyBusy
{
public static void main ( String args[] ) throws Exception
{
System.out.println ( "Demo start at " + Instant.now ( ) );
final long demoStartNanos = System.nanoTime ( );
final int countTasks = 1_000;
try (
ExecutorService executorService = Executors.newFixedThreadPool ( countTasks ) ; // We want one task per thread. So one thousand tasks means one thousand threads.
)
{
IntStream
.rangeClosed ( 1 , countTasks )
.mapToObj ( TenSecondTask :: new )
.forEach ( executorService :: submit );
}
Duration demoDuration = Duration.ofNanos ( System.nanoTime ( ) - demoStartNanos );
System.out.println ( "Demo end at " + Instant.now ( ) + ". demoDuration = " + demoDuration );
}
}
class TenSecondTask implements Runnable
{
private final int sequenceNumber;
public TenSecondTask ( final int sequenceNumber )
{
this.sequenceNumber = sequenceNumber;
}
@Override
public void run ( )
{
final long tenSecondsInNanos = Duration.ofSeconds ( 10 ).toNanos ( );
final long taskStartNanos = System.nanoTime ( );
long countLoops = 0;
while (
( System.nanoTime ( ) - taskStartNanos ) < tenSecondsInNanos
)
{
countLoops++;
}
System.out.println ( "Task # " + this.sequenceNumber + " in thread ID " + Thread.currentThread ( ).threadId ( ) + " done at " + Instant.now ( ) + ". Loops: " + String.format ( "%,d" , countLoops ) );
}
}
我的结果显示范围很广,从10万到700多万.总运行时间约为20秒,与您的代码相同.
Demo start at 2024-02-15T09:24:13.773544Z
Task # 6 in thread ID 26 done at 2024-02-15T09:24:23.779903Z. Loops: 328,186
Task # 11 in thread ID 31 done at 2024-02-15T09:24:23.812401Z. Loops: 349,456
Task # 13 in thread ID 33 done at 2024-02-15T09:24:23.839549Z. Loops: 356,229
Task # 20 in thread ID 40 done at 2024-02-15T09:24:23.883941Z. Loops: 379,757
…
Task # 991 in thread ID 1019 done at 2024-02-15T09:24:33.561808Z. Loops: 981,788
Task # 992 in thread ID 1020 done at 2024-02-15T09:24:33.566229Z. Loops: 1,034,678
Task # 993 in thread ID 1021 done at 2024-02-15T09:24:33.572045Z. Loops: 1,172,157
Task # 994 in thread ID 1022 done at 2024-02-15T09:24:33.576395Z. Loops: 1,067,262
Task # 995 in thread ID 1023 done at 2024-02-15T09:24:33.582312Z. Loops: 1,034,725
Task # 996 in thread ID 1024 done at 2024-02-15T09:24:33.586528Z. Loops: 1,134,316
Task # 997 in thread ID 1025 done at 2024-02-15T09:24:33.592593Z. Loops: 1,112,738
Task # 998 in thread ID 1026 done at 2024-02-15T09:24:34.158249Z. Loops: 6,455,185
Task # 999 in thread ID 1027 done at 2024-02-15T09:24:34.158381Z. Loops: 6,484,593
Task # 1000 in thread ID 1028 done at 2024-02-15T09:24:34.168723Z. Loops: 7,411,728
Demo end at 2024-02-15T09:24:34.168958Z. demoDuration = PT20.392301916S
顺便说一句,System.currentTimeMillis()
在几年前就被Instant.now()
取代了.在基于OpenJDK个代码库的实现上,调用Instant.now
以微秒的分辨率捕捉当前时刻,而不是在Java 9+中仅以毫秒为单位.