我需要一种方法来延迟线程精确的毫秒数(一个音乐配乐播放应用程序)。我知道Thread.sleep()
的精度不是很好,所以我决定用代替使用ScheduledExecutorService
。我这样做的方法如下:
... //some code
int duration = 100; //delay for 100ms
CountDownLatch l = new CountDownLatch(1);
Executors.newScheduledThreadPool(1).schedule(l::countDown, duration, TimeUnit.MILLISECONDS);
l.await();
... //continue execution
这是一个好方法吗?我最担心的是CountDownLatch,以及它可能增加的任何延迟(如果有的话)。
谢谢。
所以你的解决方案不是很好,因为一些问题。使用await
await将使当前线程进入睡眠状态,然后操作系统将需要在它启动之前再次调度它,这样你就失去了使用调度执行器服务所获得的优势。
我用三种不同的技术做了一个例子。一个旋转等待,使用线程。睡觉并使用你的计划执行器的想法。自旋等待时间最准确,为7002ms,而其他两种解决方案完成后超过8300ms。
import java.util.concurrent.*;
public class DwellTimes{
static public void spinWait(long ms){
long end = System.nanoTime() + ms*1000000;
long current = System.nanoTime();
while( current < end){
current = System.nanoTime();
}
}
static public void sleepWait(long ms){
try{
Thread.sleep(ms);
} catch(Exception e){
throw new RuntimeException(e);
}
}
static ScheduledExecutorService ses = Executors.newScheduledThreadPool(1);
static public void scheduleWait(long ms){
try{
CountDownLatch l = new CountDownLatch(1);
ses.schedule(l::countDown, ms, TimeUnit.MILLISECONDS);
l.await();
} catch(Exception e){
throw new RuntimeException(e);
}
}
public static void main(String[] args){
long start = System.currentTimeMillis();
for(int i = 0; i<1000; i++){
scheduleWait(7);
}
long end = System.currentTimeMillis() - start;
System.out.println( end + "ms elapsed");
}
}
对于睡眠/等待类型的流,旋转等待将是最准确的,因为它不释放线程。它只会继续滚烫。
调度执行器示例的问题是,您实际上并没有使用调度。你想安排你的任务。
public static void scheduleWork(){
CountDownLatch latch = new CountDownLatch(1000);
ses.scheduleAtFixedRate(latch::countDown, 7, 7, TimeUnit.MILLISECONDS);
try{
latch.await();
} catch(Exception e){
throw new RuntimeException(e);
}
}
最后一个示例可能是管理一致计时的最佳方法,因为您不会继续累积错误。例如,如果您的操作需要几毫秒,则下一个操作将不会延迟,除非这几毫秒超过周期。