收录日期:2021/01/23 18:27:24 时间:2010-03-31 04:41:18 标签:java,time,loops

Is there a way I can do a for loop for a certain amount of time easily? (without measuring the time ourselves using System.currentTimeMillis() ?)

I.e. I want to do something like this in Java:

int x = 0;
for( 2 minutes )  {
   System.out.println(x++);
}

Thanks

No, there isn't a built-in construct which does that.

I want to point out that you should not use System.currentTimeMillis() for performing, or delaying, a task for a specified time period. Instead use System.nanoTime(). The former method is inaccurate in Windows, while the latter method is accurate regardless of OS. You can use TimeUnit enum to easily go between time in milliseconds, or any other time unit, to time in nanoseconds.

for (long stop=System.nanoTime()+TimeUnit.SECONDS.toNanos(2);stop>System.nanoTime();) {
  /*
   * Hammer the JVM with junk
   */
}

I think that this is what you want:

private final Thread thisThread = Thread.current();
private final int timeToRun = 120000; // 2 minutes;

new Thread(new Runnable() {
    public void run() {
        sleep(timeToRun);
        thisThread.interrupt();
    }
}).start();

while (!Thread.interrupted()) {
    // do something interesting.
}

This avoids doing repeated syscalls to get the system clock value (which can be rather expensive) and polls the current thread's interrupted flag instead (much cheaper).

EDIT

There is actually no safe alternative to polling the clock or polling a flag. In theory, you could modify the above fragment to call the deprecated Thread.stop() method instead of Thread.interrupt().

(I do NOT recommend using Thread.stop() and friends. They are flawed, and dangerous to use. I'm just posing this as a theoretical alternative.)

EDIT 2

Just to point out that using Thread.interrupt() has the advantages over setting a shared flag:

  • Thread.interrupt() will cause certain blocking I/O and synchronization methods to unblock and throw a checked exception. Updating a shared flag won't do this.

  • Some third-party libraries also check the interrupt flag to see if they should stop what they are currently doing.

  • If your loop involves calls to other methods, etc, Thread.interrupt() means that you don't need to worry about those methods can access the flag ... if they need to.

EDIT 3

Just to add that sleep(N) is not guaranteed to wake the sleeping thread up after exactly N milliseconds. But under normal circumstances, it will be reasonably close.

I made a simple, but sucky, implementation for this problem. I wanted to avoid Timer and TimeTask and ended up with this quickfix solution.

The main idea is that I simply wanted to create an independed countdown timer which I could just start and call isFinished() to check if the countdown is finished.

package RIPv3;

/**
 * Creates a countdown timer which runs its own thread and uses CountdownTimerThread,                which runs yet another
 * thread.
 *
 * start() method is called to start the countdown.
 * isFinished() method is called to check if the countdown is finished or not.
 * 
 * ! Be Aware!
 * This is a quickfix and sucky implementation.
 * There will be a small delay. This is not accurate.
 * 
 * @author Endre Vestbø
 * @version 1.0 (09.05.2011)
 */
public class CountdownTimer extends Thread {
/* Countdown timer */
private CountdownTimerThread timer;

/**
 * Creates a new timer and sets time to count down
 * @param time
 *          Time to count down
 */
public CountdownTimer(long time) {
    this.timer = new CountdownTimerThread(time);
}

public void run() {
    this.timer.start();
}

/**
 * @return
 *      False if timer is running, else true
 */
public boolean isFinished() {
    if(this.timer.getState() == Thread.State.TERMINATED)
        return true;

    return false;
}   
}

 package RIPv3;

/**
 * Used by CountdownTimer to count down time.
 * 
 * @author Endre Vestbø
 * @version 1.0  (09.05.2011)
 *
 */
public class CountdownTimerThread extends Thread {
private long time;

/** Create a new timer */
public CountdownTimerThread(long time) {
    this.time = time;
}

/**
 * Start a countdown
 * @param period
 *      Period to count down given in milliseconds
 */
public void run() {
    try {
        sleep(this.time);
    } catch (InterruptedException e) {

    }
}
}

Here is another suggestion:

public class TimerLoop {
    private final AtomicBoolean loop = new AtomicBoolean();

public void run(Runnable runnable, long duration, TimeUnit timeUnit) {
    loop.set(true);
    TimerTask task = new TimerTask() {
        @Override
        public void run() {
            loop.set(false);
        }
    };
    Timer timer = new Timer();
    timer.schedule(task, timeUnit.toMillis(duration));
    while (loop.get()) {
        runnable.run();
    }
}

}

The method executes the run() method of the provided Runnable repeatedly until the timer expires.

Considerations:

  • The time will be approximate.
  • Be careful how you implement the run() method as it will potentially consume all your CPU power.
  • The implementation is not thread-safe unless you create a new instance of the TimerLoop class for each Runnable that you would like to execute.

I don't think there is a way to loop for a certain period of time without checking to see if you've looped for a certain period of time.

Depending on your use case either of the two sleep() methods in class Thread might fit the bill, but it sounds like you have to fuss with System.currentTimeMillis(), which seems strange.

Thread thread = Thread.currentThread();
thread.sleep(10);      // 10 milliseconds
thread.sleep(12, 345); // 12 milliseconds and 345 nanoseconds.

Loops iterate over value comparison and not for duration. So without yourself assigning and comparing the system time, you cannot do it. If however you wish it be intuitive, then you can have a function that internally uses milliseconds, but takes Minutes as argument and use it.

And No, you cannot use threads or the sleep method in it, because that does not ensure the exact time. If your processor is busy with some other thread after the given time elapses, then your thread will continue to wait.

No. That is sort of a strange request considering how simple it would be to simply write a function that uses System.currentTimeMillis() (or whichever time function you choose). More context of the situation might be in order.