【问题标题】:wait x seconds or until a condition becomes true等待 x 秒或直到条件变为真
【发布时间】:2014-10-09 03:09:22
【问题描述】:

如何等待 x 秒或直到条件变为真?等待时应定期测试该条件。目前我正在使用此代码,但应该有一个简短的功能。

for (int i = 10; i > 0 && !condition(); i--) {
    Thread.sleep(1000);
}

【问题讨论】:

  • 谁将condition设置为true
  • 你不能让设置条件为真的对象调用你的另一个对象的方法,当条件为真时它应该做一些事情吗?
  • 在循环内部检查会更有意义。就像睡眠 1000 毫秒然后检查然后再睡眠 1000 毫秒等。
  • 您的问题不清楚。如果您的应用程序是单线程的(因此condition() 方法在同一个线程中运行),那么您唯一的选择是将sleep 周期从1000ms 减少到1ms,并在每1ms 后轮询condition() 方法。如果您的应用程序是多线程的,那么您应该重新设计它(可能一开始就摆脱整个循环)。

标签: java conditional-statements sleep


【解决方案1】:

假设您想要您所要求的,而不是重新设计代码的建议,您应该查看Awaitility

例如,如果您想查看是否会在接下来的 10 秒内创建文件,您可以执行以下操作:

await().atMost(10, SECONDS).until(() -> myFile.exists());

它主要用于测试,但执行特定请求的技巧,即等待调用者指定的任意条件,而无需显式同步或睡眠调用。如果您不想使用该库,只需阅读代码以了解它的工作方式。

在这种情况下,这归结为与问题类似的轮询循环,但将 Java 8 lambda 作为参数传入,而不是内联条件。

【讨论】:

【解决方案2】:

我没有在 JDK 中找到解决方案。我觉得这个功能应该加到JDK里面去。

这是我使用功能接口实现的:

import java.util.concurrent.TimeoutException;
import java.util.function.BooleanSupplier;

public interface WaitUntilUtils {

  static void waitUntil(BooleanSupplier condition, long timeoutms) throws TimeoutException{
    long start = System.currentTimeMillis();
    while (!condition.getAsBoolean()){
      if (System.currentTimeMillis() - start > timeoutms ){
        throw new TimeoutException(String.format("Condition not met within %s ms",timeoutms));
      }
    }
  }
}

【讨论】:

  • 如果在您等待条件时更改了系统挂钟,如果条件从未满足,您可能会遇到死锁(或提前等待)。对于这种情况,您应该使用单调时钟。
  • 很好,谢谢。我猜夏季/冬季时间也可能是一个问题。我主要用它来测试。直到一些非常有用的东西!
【解决方案3】:

您是否考虑过 java.util.concurrent 中的一些类 - 例如 BlockingQueue? 你可以使用:

BlockingQueue<Boolean> conditionMet = new BlockingQueue<Boolean>;
conditionMet.poll(10,TimeUnit.SECONDS);

然后在更改条件的代码中执行以下操作:

conditionMet.put(true);

编辑:

另一个例子 java.util.concurrent 可能是 CountDownLatch:

CountDownLatch siteWasRenderedLatch = new CountDownLatch(1);
boolean siteWasRendered = siteWasRenderedLatch.await(10,TimeUnit.SECONDS);

这样您将等待 10 秒或直到闩锁达到零。要达到零,您只需:

siteWasRenderedLatch.countDown();

这样您就不需要使用@Adrian 提供的条件示例中需要的锁。我认为这更简单直接。

如果您不喜欢命名“Latch”或“Queue”,您可以随时将其包装到您自己的名为 LimitedTimeCondition 的类中:

public class LimitedTimeCondition
{
    private CountDownLatch conditionMetLatch;
    private Integer unitsCount;
    private TimeUnit unit;

    public LimitedTimeCondition(final Integer unitsCount, final TimeUnit unit)
    {
        conditionMetLatch = new CountDownLatch(1);
        this.unitsCount = unitsCount;
        this.unit = unit;
    }

    public boolean waitForConditionToBeMet()
    {
        try
        {
            return conditionMetLatch.await(unitsCount, unit);
        }
        catch (final InterruptedException e)
        {
            System.out.println("Someone has disturbed the condition awaiter.");
            return false;
        }

    }

    public void conditionWasMet()
    {
        conditionMetLatch.countDown();
    }
}

而用法是:

LimitedTimeCondition siteRenderedCondition = new LimitedTimeCondition(10, TimeUnit.SECONDS);
//
...
//
if (siteRenderedCondition.waitForConditionToBeMet())
{
   doStuff();
}
else
{
   System.out.println("Site was not rendered properly");
}
//
...
// in condition checker/achiever:
if (siteWasRendered)
{
   condition.conditionWasMet();
}

【讨论】:

  • 不要认为这可能有效;调用 conditionMet.put(false) 会发生什么?
  • 您可以将某些内容放入队列中,而不是将条件变量设置为 true(然后在方法 'condition()' 中返回它)。它可能是布尔值(无论是真还是假)、整数、字符串 - 无论你想要什么。如果条件不满足,就不要放任何东西。我们对这里的条件一无所知,所以这是可以从 java.util.concurrent 使用的建议之一。
  • 我添加了另一个可能更适合/直观的示例。
  • 如果您阅读该问题,您会看到他正在等待外部进程。您的代码将在线程 A 中进行轮询,然后使用这些东西将结果传递给线程 B,该线程 B 被阻塞。这并不能解决问题——它只展示了如何在 A 和 B 之间进行通信,而不是如何写 A。如果你知道如何写 A,你就不需要 B。
  • 另外,第三个示例不仅比他想要缩短的代码长 20 倍,而且我认为它会使多余的线程在超时时运行。
【解决方案4】:

看看Condition

条件(也称为条件队列或条件变量) 提供一种方法让一个线程暂停执行(“等待”)直到 由另一个线程通知某些状态条件现在可能为真。 因为对这个共享状态信息的访问发生在不同的 线程,它必须被保护,所以某种形式的锁是相关联的 与条件。等待条件的关键属性 提供的是它以原子方式释放关联的锁,并且 暂停当前​​线程,就像 Object.wait 一样。

条件实例本质上绑定到锁。获得一个 特定 Lock 实例的条件实例使用其 newCondition() 方法。

编辑:

【讨论】:

  • 如果你只有一个线程,那么如果只有一个线程在等待,一个条件怎么会变成真的呢?
  • @Martin Nielsen condition() 是一个函数。
  • 但是如果程序只有一个线程,当线程被sleep方法捕获时,会调用condition()吗?
  • 提问者可能不需要多头进程来做他想做的事情..如果让他的程序“暂停”几秒钟不会产生“用户体验”问题。请参阅下面的替代答案。
  • 这根本不能回答问题。
【解决方案5】:

您可能想要使用类似下面的代码(其中secondsToWait 包含您希望等待的最大秒数,以查看condition() 是否变为真。如果条件为,变量isCondetionMet 将包含真found,如果代码等待条件超时,则返回 false。

        long endWaitTime = System.currentTimeMillis() + secondsToWait*1000;
        boolean isConditionMet = false;
        while (System.currentTimeMillis() < endWaitTime && !isConditionMet) {
            isConditionMet = condition();
            if (isConditionMet) {
                break;
            } else {
                Thread.sleep(1000);
            }
        }

【讨论】:

  • 这比原始代码长了一点,没有明显的好处。
  • 这段代码有几个优点:它保留了有关循环如何退出的信息;它更清楚地显示了这段代码“暂停”的时间......如果在单线程环境中特别有用。
【解决方案6】:

我正在使用原始问题解决方案的以下改编:

public class Satisfied {
    public static boolean inTime(Callable<Boolean> condition, int timeoutInSecs) {
        int count;
        try {
            for (count = 1; count < timeoutInSecs * 20 && !condition.call(); count++)
                Thread.sleep(50);
            return (count < timeoutInSecs * 20);
        } catch (Exception e) {
            throw new AssertionError(e.getMessage());
        }
    }
}

在测试中使用时是这样的:

assertThat(Satisfied.inTime(() -> myCondition(), 5)).isTrue();

【讨论】:

    猜你喜欢
    • 2015-09-24
    • 1970-01-01
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多