Java循环一定时间

15

有没有一种简单的方法可以在特定时间内执行for循环?(不使用System.currentTimeMillis()来计算时间)

例如,我想在Java中像这样做:

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

谢谢


4
你想要在屏幕上持续疯狂地打印某些内容两分钟吗?还是每两分钟打印一次? - Bruno Reis
1
我想在屏幕上持续疯狂地打印某些内容,持续时间为2分钟。 - Lydon Ch
5
大多数答案都是完全错误的,说你不能在不测量时间的情况下完成它。你绝对可以在不使用System.currentTimeMillis()的情况下完成它。例如,您可以安排一个定时器,设置为两分钟,更改一个易失性布尔变量(比如shouldStop),并使用*while(!shouldStop){...},但对于这么简单的事情,我不明白为什么要避免使用System.currentTimeMillis()*(请注意,即使我更喜欢使用System.currentTimeMillis(),也不会改变大多数答案完全错误且被错误地点赞的事实)。 - SyntaxT3rr0r
1
顺便说一下,Java不是实时的,即使不断检查System.currentTimeMillis(),也不能保证你能够运行恰好两分钟:你的线程可能会被“调度出去”,你可能会错过“精确”的两分钟时间点,因此认为计时器可能会错过标记是相当愚蠢的,因为一个不断检查System.currentTimeMillis()的线程也可能会错过标记 :) - SyntaxT3rr0r
哦,WizardOfOdds,如此热情,如此愤怒。我有一个朋友想认识你。 - Tim Bender
显示剩余2条评论
10个回答

32

没有内置的结构可以实现这个。

我想指出,您不应该使用System.currentTimeMillis()来执行或延迟指定时间段的任务。而应该使用System.nanoTime()。前者在Windows中不准确,而后者无论操作系统如何都是准确的。您可以使用TimeUnit枚举轻松地在毫秒或任何其他时间单位之间转换成纳秒。

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

谢谢。我现在只是好奇时间检查需要多长时间。 - Lydon Ch
哦不。使用纳秒并不意味着可以像那样浪费CPU周期!永远不要编写一个循环,监视时间并且什么都不做!相反:使用Thread.sleep(...)允许系统在您不需要它时执行有用的操作!! - Zordid
1
@Zordid - OP希望在此循环期间执行一些有用的操作,因此我在循环中放置了注释。@portoalet - 我不确定,它只是检索高分辨率计时器,而不是另一个计时器。因此,它应该与System.currentTimeMillis相同。 - Tim Bender
1
确实,相对于 nanoTime()System.currentTimeMillis() 的精度较低,但仍然有约10-15毫秒。因此,除非您想测量在毫秒范围内的时间,否则使用 System.currentTimeMillis() 完全没有问题。此外,我认为 System.currentTimeMillis()nanoTime() 稍微快一些(尽管这取决于 JVM,通常不会产生差异)。 - sleske
@Hirosht 输入以秒为单位,由 TimeUnit.SECONDS 枚举所规定。请参阅:https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/TimeUnit.html - Tim Bender
显示剩余4条评论

14
我认为这就是你想要的:
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.
}

这样做避免了重复系统调用以获取系统时钟值(这可能非常昂贵),而是轮询当前线程的interrupted标志(成本更低)。

编辑

实际上没有安全的替代方案来轮询时钟或轮询标志。理论上,您可以修改上面的片段,使用已弃用Thread.stop()方法而不是Thread.interrupt()

(我不建议使用Thread.stop()等方法。它们存在缺陷,使用起来很危险。我只是提出这个作为一个理论上的替代方案。)

编辑2

仅指出使用Thread.interrupt()相对于设置共享标志具有以下优点:

  • Thread.interrupt()将导致某些阻塞I/O和同步方法取消阻塞并抛出已检查异常。更新共享标志无法做到这一点。

  • 某些第三方库还会检查中断标志以查看它们是否应该停止当前正在执行的操作。

  • 如果您的循环涉及调用其他方法等,则Thread.interrupt()意味着您不需要担心这些方法可以访问标志......如果它们需要。

编辑3

仅补充说明sleep(N)不能保证在N毫秒后唤醒正在睡眠的线程。但在正常情况下,它会相当接近。


3

我为这个问题做了一个简单但不太好的实现。我想避免使用Timer和TimeTask,结果用了这个临时解决方案。

主要思路是我只想创建一个独立的倒计时器,可以随时启动并调用isFinished()来检查倒计时是否结束。

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) {

    }
}
}

1

这里有另一个建议:

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

}

该方法会重复执行提供的Runnablerun()方法,直到计时器到期。

注意事项:

  • 时间将是近似的。
  • 请注意如何实现run()方法,因为它可能会消耗所有CPU资源。
  • 除非您为要执行的每个Runnable创建TimerLoop类的新实例,否则实现不是线程安全的。

1
不。考虑到只需编写一个使用System.currentTimeMillis()(或您选择的其他时间函数)的函数,这样的请求有点奇怪。可能需要更多情境信息。

+0.5 秒的奖励是因为你比我快了23秒。+0.5 秒的奖励是因为你指出了请求的怪异之处。 - MaxGuernseyIII
@MaxGuernseyll - 实际上,调用System.currentTimeMillis()可能会非常昂贵。在某些平台上,它将涉及进行系统调用;即数百条机器指令。 - Stephen C
3
奇怪的请求并不意味着问题很傻?如果您这么说的话...不管怎样,“否”显然是一个错误的答案。 - Stephen C
@Stephen 如果我们使用 System.currentTimeMillis(),平均每秒会进行多少个系统调用?哪个平台的性能更好,即所需的系统调用最少? - Lydon Ch
@portolet - 我不能给你这些问题一个明确的答案。 - Stephen C

0
LocalDateTime then = LocalDateTime.now();
while (true) {


// logic
if (ChronoUnit.SECONDS.between(then, LocalDateTime.now()) >= 20) break;
}

你的回答可以通过添加更多关于代码的信息以及它如何帮助提问者来改进。 - Tyler2P

0

我认为没有一种方法可以在不检查是否已经循环了一定时间的情况下循环一段时间。


1
好的,某个人,某个地方必须检查时间。即使您使用ScheduledFuture、TimerTask或甚至是Quartz组件,在该代码内部肯定会有一个计时机制。 - Adriaan Koster
@Adriaan - 不一定。在Windows和Linux上,C语言的sleep库函数是作为系统调用实现的;请参见http://cboard.cprogramming.com/c-programming/109721-sleep-system-call.html。我预计Java Thread.sleep(...)在底层调用了C库函数。 - Stephen C
@MaxGuernseylll - 当然,sleep系统调用与时间有关。但是机制不在ScheduledFuture等代码内部,而是在操作系统内核中。它绝对不涉及“检查是否已循环一定时间”。 - Stephen C
@MaxGuernseylll - 顺便说一下,我已经将你的评论标记为冒犯性言论。请不要暗示我使用非法毒品。 - Stephen C
@Max @Stephen,版主需要锁定帖子吗? - user1228

0
根据您的使用情况,Thread类中的两种sleep()方法中的任何一种都可能适合您的需求,但听起来您需要烦恼System.currentTimeMillis(),这似乎很奇怪。
Thread thread = Thread.currentThread();
thread.sleep(10);      // 10 milliseconds
thread.sleep(12, 345); // 12 milliseconds and 345 nanoseconds.

不幸的是,Thread.sleep需要一个建议的休眠时间作为参数...它应该至少休眠那么多毫秒,而不是恰好。你仍然需要检查实际经过了多少时间。 - billjamesdev

0

循环是基于值比较,而不是持续时间。因此,如果你不自己分配和比较系统时间,就无法实现这一点。 然而,如果你希望它直观一些,那么可以有一个内部使用毫秒的函数,但以分钟为参数并使用它。

并且,不,你不能在其中使用线程或sleep方法,因为那不能确保精确的时间。如果在给定时间过后你的处理器忙于其他线程,那么你的线程将继续等待。


0
在你的问题中提到了以下内容:
(不使用System.currentTimeMillis()自己测量时间?)
以下是如何做到这一点,而不使用任何系统时间。别忘了检查代码示例。
1. 定义一个本地对象数组,供您的线程操作。 2. 创建一个新的线程。 3. 在run方法中使用sleep(1000*15)来暂停15秒。 4. 更新您的本地变量。
在这里可以查看一个很好的代码示例 https://dev59.com/unI-5IYBdhLWcg3wEEDu#71611647

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接