如何在线程上实现保证的睡眠时间

Man*_*tel 4 java multithreading

我要求每50毫秒调用一个类方法.我不使用,Thread.sleep因为它非常重要,它尽可能精确地发生在milli,而sleep只保证最短的时间.基本设置如下:

public class ClassA{
    public void setup(){
        ScheduledExecutorService se = Executors.newScheduledThreadPool(20);
        se.scheduleAtFixedRate(this::onCall, 2000, 50, TimeUnit.MILLISECONDS);
    }

    protected void onCall(Event event) {
        // do something
    }
}
Run Code Online (Sandbox Code Playgroud)

现在这个大体上工作正常.我已经把System.out.println(System.nanoTime)onCall检查其被称为精确到我希望它是.我发现在100次通话过程中有一个1-5毫秒的漂移,这会一次又一次地纠正.

不幸的是,5毫秒漂移对我来说非常重要.1毫米的漂移是可以的,但是在5毫秒时,onCall由于其他物体的状态,它会影响我正在进行的计算.如果我能让调度程序自动纠正,如果一次调用晚了5ms,下一次将在45ms而不是50ms发生,那几乎可以.

我的问题是:在Java中有没有更精确的方法来实现这一目标?我现在能想到的唯一解决方案是check每1ms 调用一个方法并检查时间,看它是否在50ms标记处.但是,如果在偶然情况下错过了精确的50ms间隔(49,51),那么我需要保持一些逻辑.

谢谢

Ste*_*n C 7

我可以在线程上保证睡眠时间吗?

对不起,不行.

在Java SE JVM中无法获得可靠,精确的延迟时序.您需要使用在实时操作系统上运行的实时Java实现.


以下是普通操作系统上的Java SE无法执行此操作的几个原因.

  1. 在某些时候,Java SE JVM中的GC需要"阻止世界".发生这种情况时,没有用户线程可以运行.如果您的计时器在"停止世界"暂停时关闭,则在暂停结束之前无法安排计时器.

  2. JVM中的线程调度实际上是由主机操作系统完成的.如果系统繁忙,主机操作系统可能会决定在应用程序需要时不调度JVM的线程.


这种java.util.Timer.scheduleAtFixedRate方法可能与您在Java SE上的表现一样好.它应该解决长期漂移,但你无法摆脱"抖动".而且这种抖动可能很容易达到几百毫秒......甚至几秒钟.

如果系统繁忙且操作系统正在抢占或未安排线程,则自旋锁将无法提供帮助.(并且用户代码中的自旋锁定是浪费...)