Thread.sleep() 的精确替代
Precision alternative to Thread.sleep()
我需要一种方法来将线程延迟精确的毫秒数(乐谱播放应用程序)。我知道 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,以及它可能增加的任何延迟(如果有的话)。
谢谢。
所以你的解决方案不是很好,因为有几个问题。您正在os利用计划的执行程序服务获得的优势,方法是使用 await
await 将使当前线程进入休眠状态,然后 os 将需要安排在它开始之前再说一遍。
我使用三种不同的技术制作了一个示例。旋转等待,使用 thread.sleep 并使用您计划的执行程序的想法。自旋等待是 most 的精确度,在 7002 毫秒,而其他两个解决方案在完成时超过 8300 毫秒。
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");
}
}
对于 sleep/wait 风格的流,自旋等待将是 most 的精确度,因为它不会释放线程。它只会继续 运行 火爆。
您的计划执行程序示例的问题在于您实际上并未使用计划。你想安排你的任务。
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);
}
}
最后一个示例可能是管理一致计时的最佳方式,因为您不会继续累积错误。例如,如果你的动作需要几毫秒,下一个动作不会延迟除非这几毫秒os超过周期。
我需要一种方法来将线程延迟精确的毫秒数(乐谱播放应用程序)。我知道 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,以及它可能增加的任何延迟(如果有的话)。
谢谢。
所以你的解决方案不是很好,因为有几个问题。您正在os利用计划的执行程序服务获得的优势,方法是使用 await
await 将使当前线程进入休眠状态,然后 os 将需要安排在它开始之前再说一遍。
我使用三种不同的技术制作了一个示例。旋转等待,使用 thread.sleep 并使用您计划的执行程序的想法。自旋等待是 most 的精确度,在 7002 毫秒,而其他两个解决方案在完成时超过 8300 毫秒。
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");
}
}
对于 sleep/wait 风格的流,自旋等待将是 most 的精确度,因为它不会释放线程。它只会继续 运行 火爆。
您的计划执行程序示例的问题在于您实际上并未使用计划。你想安排你的任务。
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);
}
}
最后一个示例可能是管理一致计时的最佳方式,因为您不会继续累积错误。例如,如果你的动作需要几毫秒,下一个动作不会延迟除非这几毫秒os超过周期。