简体   繁体   中英

Precision alternative to Thread.sleep()

I need a way to delay a thread for a precise number of milliseconds (a music score playing application). I know that the precision of Thread.sleep() is not very good, so I decided to instead use ScheduledExecutorService . The way I do this is below:

... //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

Is this a good approach? I am mostly worried about the CountDownLatch, and any delay (if any) it may add.

Thanks.

So your solution is not good because of a few issues. You're losing the advantage you gain from using a scheduled executor service by using await await is going to put the current thread to sleep, and then the os will need to schedule it again before it starts.

I've made an example using three different techniques. A spin wait, using thread.sleep and using your scheduled executor idea. The spin wait is the most accurate, at 7002ms where the other two solutions are over 8300ms when finished.

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");
    }
}

For a sleep/wait style of flow, the spin wait will be the most accurate because it doesn't release the Thread. It's just going to continue running hot.

The problem with your scheduled executor example is that you're not actually using the scheduling. You want to schedule your tasks.

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);
    }
}

This last example is probably the best way to manage consistent timing because you won't continue to accumulate errors. Eg, if your action takes a couple ms, the next action will not be delayed unless those couple of ms exceed the period .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM