等待 x 秒或直到条件变为真

2022-09-01 16:30:08

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

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

答案 1

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

例如,如果要查看是否将在接下来的 10 秒内创建文件,请执行如下操作:

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

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

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


答案 2

你有没有想过java.util.concurrent中的一些类 - 例如BlocklingQueue?您可以使用:

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