Java高分辨率睡眠

6mw9ycah  于 2023-05-05  发布在  Java
关注(0)|答案(1)|浏览(117)

我想在Java中模拟一个高频率的时钟。例如,我们可以将其视为1 Mhz CPU时钟。这可以通过以下代码实现:

// 1000 nanoseconds between ticks.
    final long period = 1000;
    this.runnable = new Runnable() {
        public void run() {
            long last = System.nanoTime();
            while (true) {
                long now = System.nanoTime();
                if (now - last > period) {
                    last = now;
                    System.out.println(Thread.currentThread().getId() + " ticked at:" + now);
                }
            }

        }
    };
    this.thread = new Thread(this.runnable);
    this.thread.start();

这段代码的问题在于,它是一个高CPU消耗者,因为线程一直在运行。我想让线程在随后的时间检查之间休眠一段时间,类似于下面的代码:

// 1000 nanoseconds between ticks.
    final long period = 1000;
    this.runnable = new Runnable() {
        public void run() {
            long last = System.nanoTime();
            while (true) {
                long now = System.nanoTime();
                if (now - last > period) {
                    last = now;
                    System.out.println(Thread.currentThread().getId() + " ticked at:" + now);
                }
            }
            // here 4 is arbitrary, it determines checks frequency
            LockSupport.parkNanos(period / 4);
        }
    };
    this.thread = new Thread(this.runnable);
    this.thread.start();

这种方法的问题是线程暂停的时间太长(在Windows中〉1 ms)。如果我使用Thread.sleep(0, period / 4);,也会发生同样的事情。我知道Thread.sleepLockSupport.parkNanos只能保证最短的睡眠/停止时间。有没有办法达到我的目的,即:以这个频率勾选锁,而不使线程不断运行?

jmo0nnb3

jmo0nnb31#

ScheduledExecutorService

在现代Java中,我们很少需要直接寻址Thread。使用Java 5中添加的Executor框架。
该框架的一部分是ScheduledExecutorService。您可以要求在指定的时间段之间重复执行任务。
一些要点:

  • 显然,你的任务的代码必须非常简短,在不到一微秒的时间内执行。这很可能是不切实际的。
  • 您可以选择两种计划方法:scheduleAtFixedRatescheduleWithFixedDelay。我猜你想要第一个。但是你要研究这两个问题来决定你自己。
  • 你的任务的时间安排将不会是完美的。主机OS和JVM都可能引入延迟。如果你想要完美的计时,你必须使用real-time system
  • 我怀疑你所期望的一微秒的时间范围正在接近硬件时钟和软件调度程序的极限。在这个时间粒度上,我不知道这种方法是否能令人满意地满足您的需求。
  • 您必须小心关闭您的executor服务。否则它的后备线程池可能会无限期地运行,就像一个僵尸🧟‍♂️。这里我们使用try-with-resources语法来自动关闭。

下面是Java 20中的一些示例代码。

AtomicLong count = new AtomicLong( 0L );
try (
        ScheduledExecutorService ses = Executors.newSingleThreadScheduledExecutor() ;
)
{
    Runnable task = count :: incrementAndGet;
    ses.scheduleAtFixedRate( task , 0L , 1L , TimeUnit.MICROSECONDS ); // 1,000 nanos = 1 micro.
    try { Thread.sleep( Duration.ofMillis( 10 ) ); } catch ( InterruptedException e ) { throw new RuntimeException( e ); }
}
// We expect around 1,000 per millisecond in our count.
System.out.println( "count = " + count );

运行时:
计数= 13226
我们预计每毫秒大约有1,000个计数。在10毫秒内,我们预计会有大约10,000个。在Apple M1 MacBook Pro上运行Java 20中的IntelliJ时,我得到的计数结果为10,xxx到13,xxx。

相关问题