24

I have a requirement to pause a while loop for a specific number of milliseconds. I have tried using Thread.sleep(duration) but it is not accurate, especially in a looping scenario. Millisecond accuracy is important in my program.

Here is the algorithm where I don't want to go back to check for condition until expectedElapsedTime has passed by.

while (condition) {
    time = System.currentTimeMillis();
    //do something
    if (elapsedTime(time) < expectedElapsedTime) ) {
        pause the loop  // NEED SUBSTITUTE FOR Thread.sleep()
    }
    // Alternative that I have tried but not giving good results is 
    while ((elapsedTime(time) < expectedElapsedTime)) {
        //do nothing
    }
}

long elapsedTime(long time) {
   long diff = System.currentTimeMillis() - time;
   return diff;
}
Gray
  • 115,027
  • 24
  • 293
  • 354
user1189747
  • 241
  • 1
  • 2
  • 3

7 Answers7

12

Try a ScheduledThreadPoolExecutor. It's supposed to give more reliable timing results.

BillRobertson42
  • 12,602
  • 4
  • 40
  • 57
  • This is the recommended way now (Java 6). This way you don't need to do continuous polling and would not be required to use a while loop. You then put the code you need to run periodically in a Runnable object. – Alastair Feb 04 '12 at 19:41
  • How does using a ScheduledThreadPoolExecutor help him pause his while loop? – Perception Feb 04 '12 at 20:03
  • 2
    It doesn't, but it solves the real requirement of doing work at a accurate intervals. – BillRobertson42 Feb 04 '12 at 23:08
  • Here is the code that I tried with ScheduledThreadPoolExecutor. It is kind of a dirty way to pause the loop but would like to know if there is anything wrong with it because the pause is not accurate. while () { dummycounter = 10; //This should pause the loop for dummyCounter millisec -- not working executor.schedule((new Runnable (){public void run() { }}),dummyCounter,TimeUnit.MILLISECONDS); } – user1189747 Feb 15 '12 at 06:21
  • @Bill: Can you do this when the time interval changes? I mean, for accuracy, you have to take into account the machine working. – Luis A. Florit Dec 08 '13 at 23:48
  • 1
    @LuisA.Florit I'm not sure what you're asking. Maybe you should submit a question? – BillRobertson42 Dec 09 '13 at 17:55
10

What do you expect?

If you go to sleep then once your process is again runable it will have to wait for the thread scheduler to schedule it again.

I mean if you go to sleep for 50 seconds that does not mean that your process will be running in exactly 50 seconds.Because ater it wakes and is runnable it will have to wait to be scheduled to CPU which takes extra time plus the time you have for context switch.

There is nothing you can do to control it so you can not have the accuracy you are saying.

For your case I would suggest a spinning loop instead.

long now = System.currentTimeMillis();   
while(now < expectedElapsedTime){
    now = System.currentTimeMillis();
}
beckah
  • 1,543
  • 6
  • 28
  • 61
Cratylus
  • 52,998
  • 69
  • 209
  • 339
  • This wouldnt give 100% accuracy either from my understanding right? Wouldn't it be better to make a different thread that handles it? – warbio Feb 04 '12 at 19:35
  • @aleroot:Yes of course it will wastecycles.But in any case it will not be for long.He says miliseconds – Cratylus Feb 04 '12 at 19:37
  • 1
    @warbio:No.Another thread is no option as you have the overhead of context switch (at the very list) – Cratylus Feb 04 '12 at 19:37
  • As I had mentioned in my original code post, I had tried this option, it is giving better results than Thread.sleep but it does go a bit off few times. – user1189747 Feb 05 '12 at 08:04
  • @user1189747:In your OP you have in a comment that "it does not give good results".I did not see anywhere updating the current time and I assumed that you did it wrong.Anyway, if you give up the CPU via a `sleep` or a `wait` it is not possible to get it back in a predictable way for reasons I explain in my answer.The only way is to do busy waiting.So your best approach is the while loop.Now if even this loop sometimes is off, this could be due to the use of the `System.currentMillisec()`.Use a while loop but simulate time instead.I mean find a value that suits you and keep incrementing. – Cratylus Feb 05 '12 at 08:42
  • @user1189747:Just increment a counter and once you hit the limit you exit the `while` loop.The idea is to simulate the elapsed time via counter somehow so that the `while` loop is as tight as possible. – Cratylus Feb 05 '12 at 08:43
  • Wanted to mention that I have multiple instances of this thread and just putting a while loop as above actually spiked up the machine performance. – user1189747 Feb 15 '12 at 07:44
  • @user1189747:Yes of course since by using polling you have threads that are CPU bound.But from your comment I am not sure if this is a complaint from you or not – Cratylus Feb 15 '12 at 16:25
  • Yup, you can call it a complaint ! This solution is not working for me – user1189747 Feb 15 '12 at 17:41
  • 1
    Well you can't have it all.Sorry.Either you do some kind of `sleep` or `wait` which means you yield the CPU and as a result your threads are not CPU bound and you don't see that spike you say BUT you can NOT have the accuracy in ms you need (after you give up CPU you can't control exactly when you get it back-it is not possible) OR you use polling and have threads that are CPU bound and the CPU works constantly but since you don't give up the CPU you have as much accuracy as possible BUT you'll have the spike you complaint about.Take a peek on which trade-off you are willing to live with – Cratylus Feb 15 '12 at 20:32
  • Yep, this is the only way I was able to make my images to scroll smoothly. The processor gets very busy, but at least the animation is smooth now. In my tests (Galaxy Note), `Thread.sleep()` is not useful for less than 100ms. – Luis A. Florit Dec 08 '13 at 23:43
7

Java is not a real-time system, you can not make a thread go away and come back on such a tight schedule. To schedule your program execution down to millisecond you need to use a different platform - like simpleRTJ or Java Real-Time extension.

jprusakova
  • 1,557
  • 3
  • 19
  • 31
2

The delay is likely to arbitrarily chosen, so I would question your need to real time interval timing.

If you need read time you need to busy wait for the time to reached. Giving up the CPU means you can't guarantee you will get it back exactly when you want.

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
1

If you want to be accurate with sounds you use a sequencer and set the tempo in BPM: sequencer.setTempoInBPM(120);

1

You could implement wait/notify mechanism and delegate to another thread the responsibility of notify the other thread in wait state that the amount of time is passed and that it can go ahead ...

For example when the threadA need to wait for a certain amount of time you can put the thread in wait state and start a timer task that after a certain amount of time (interval ) call notify and wake up the ThreadA that go ahead, this could be an alternative .

aleroot
  • 71,077
  • 30
  • 176
  • 213
  • If the thread `waits` then it is in blocked state.This means context switch which means extra time, which as I understand the OP he does wants strict precision – Cratylus Feb 04 '12 at 19:33
  • This could be an alternative, i don't know if it is more accurate or not, should be tested ... – aleroot Feb 04 '12 at 19:34
  • 1
    I tried this.wait(millisec,nanosec) , it is no better than Thread.sleep. – user1189747 Feb 15 '12 at 05:04
  • Also, I read tried the solution of having a daemon thread as suggested in http://stackoverflow.com/questions/824110/accurate-sleep-for-java-on-windows. – user1189747 Feb 15 '12 at 05:41
  • Also, I read tried the solution of having a daemon thread as suggested in http://stackoverflow.com/questions/824110/accurate-sleep-for-java-on-windows. – user1189747 Feb 15 '12 at 05:42
  • sorry, about multiple posts.Continuing... I looked at the interrupt/sec on my window7 machine and it is 1000 interrupt/sec. I still tried starting the daemon thread but it did not make a difference. – user1189747 Feb 15 '12 at 05:44
0

The solution is to use a handler with a runnable and use of the method 'postDelayed'. Example:

new Handler().postDelayed(new Runnable() {
public void run () {
    // Do delayed stuff!
}
}, 5000L); //5 seconds delay 

https://stackoverflow.com/a/21680858

Community
  • 1
  • 1