睡眠并检查直到条件为真

Posted

技术标签:

【中文标题】睡眠并检查直到条件为真【英文标题】:Sleep and check until condition is true 【发布时间】:2013-01-06 00:53:02 【问题描述】:

Java 中是否有执行以下操作的库? thread 应该重复 sleep x 毫秒,直到条件变为真或达到最大时间。

这种情况主要发生在测试等待某个条件变为真时。条件受另一个thread 影响。

[编辑]为了更清楚,我希望测试在失败之前只等待 X 毫秒。它不能永远等待条件变为真。我正在添加一个人为的示例。

class StateHolder
    boolean active = false;
    StateHolder()
        new Thread(new Runnable()
            public void run()
                active = true;
            
        , "State-Changer").start()
    
    boolean isActive()
        return active;
    



class StateHolderTest
    @Test
    public void shouldTurnActive()
        StateHolder holder = new StateHolder();
        assertTrue(holder.isActive); // i want this call not to fail 
    

【问题讨论】:

如果有另一个线程控制它,你能用等待/通知而不是轮询设置一个实际的“完成”信号吗? 条件受另一个线程影响 我可以说我可以在这里使用线程连接吗?无论如何 +1 的问题。 【参考方案1】:

你不应该睡觉和检查,睡觉和检查。你想等待一个条件变量,让条件发生变化,并在该做某事的时候唤醒线程。

【讨论】:

请看我改进的描述。【参考方案2】:

编辑

大多数答案都集中在带有等待和通知或条件的低级 API(它们的工作方式或多或少相同):当您不习惯它时,很难做到正确。证明:其中 2 个答案没有正确使用 wait。java.util.concurrent 为您提供了一个高级 API,其中所有这些复杂性都被隐藏了。

恕我直言,当并发包中存在实现相同功能的内置类时,使用等待/通知模式毫无意义。


初始计数为 1 的 CountDownLatch 正是这样做的:

当条件成立时,调用latch.countdown(); 在您的等待线程中,使用:boolean ok = latch.await(1, TimeUnit.SECONDS);

人为的例子:

final CountDownLatch done = new CountDownLatch(1);

new Thread(new Runnable() 

    @Override
    public void run() 
        longProcessing();
        done.countDown();
    
).start();

//in your waiting thread:
boolean processingCompleteWithin1Second = done.await(1, TimeUnit.SECONDS);

注意:CountDownLatches 是线程安全的。

【讨论】:

【参考方案3】:

您应该使用Condition

如果您想在条件之外设置超时,请参阅await(long time, TimeUnit unit)

【讨论】:

【参考方案4】:

看起来你想要做的是检查条件,然后如果它是假的,等到超时。然后,在另一个线程中,一旦操作完成,就通知All。

服务员

synchronized(sharedObject)
  if(conditionIsFalse)
       sharedObject.wait(timeout);
       if(conditionIsFalse) //check if this was woken up by a notify or something else
           //report some error
       
       else
           //do stuff when true
       
  
  else
      //do stuff when true
  

转换器

  synchronized(sharedObject)
   //do stuff to change the condition
   sharedObject.notifyAll();
 

这应该对你有用。您也可以使用自旋锁来执行此操作,但每次通过循环时都需要检查超时。代码实际上可能更简单一些。

【讨论】:

你不应该在while循环之外使用wait。【参考方案5】:

我一直在寻找像 Awaitility 提供的解决方案。我想我在我的问题中选择了一个不正确的例子。我的意思是在您期望发生由第三方服务创建的异步事件并且客户端无法修改服务以提供通知的情况下。下面是一个更合理的例子。

class ThirdPartyService 

    ThirdPartyService() 
        new Thread() 

            public void run() 
                ServerSocket serverSocket = new ServerSocket(300);
                Socket socket = serverSocket.accept();
                // ... handle socket ...
            
        .start();
    


class ThirdPartyTest 

    @Before
    public void startThirdPartyService() 
        new ThirdPartyService();
    

    @Test
    public void assertThirdPartyServiceBecomesAvailableForService() 
        Client client = new Client();
        Awaitility.await().atMost(50, SECONDS).untilCall(to(client).canConnectTo(300), equalTo(true));
    


class Client 

    public boolean canConnect(int port) 
        try 
            Socket socket = new Socket(port);
            return true;
         catch (Exception e) 
            return false;
        
    

【讨论】:

准确地说,Condition 或其他基于 Java 异步性的解决方案最终会使代码变得混乱,并且对于没有为后台处理提供同步等待的任何入口点的 3rd 方库的情况,读起来真的很不自然.即使在非测试代码中,我最终也使用了awaitility,因为它是这种情况下最适合的解决方案,尤其是从可读性/可维护性的角度来看。【参考方案6】:

Awaitility 提供了一个简单而干净的解决方案:

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

【讨论】:

【参考方案7】:

我通过使用功能接口应用这种通用方法解决了这个问题:

private void waitUntilConditionIsMet(BooleanSupplier awaitedCondition, int timeoutInSec) 
    boolean done;
    long startTime = System.currentTimeMillis();
    do 
        done = awaitedCondition.getAsBoolean();
     while (!done && System.currentTimeMillis() - startTime < timeoutInSec * 1000);

然后就可以实现了:

class StateHolderTest
    @Test
    public void shouldTurnActive()
        StateHolder holder = new StateHolder();
        waitUntilConditionIsMet(() -> holder.isActive(), 10);
        assertTrue(holder.isActive);
    

【讨论】:

【参考方案8】:

您可以使用类似的方法,但将“true”替换为您要检查的条件,它会一直休眠直到它变为 true。

sleepUntil(() -> true);

//Sleeps this thread until the condition is true.
public static void sleepUntil(BooleanSupplier condition) 
    while(!condition.getAsBoolean()) 
        sleep(10);
    


//Sleeps this thread with the given amount of milliseconds
public static void sleep(int ms) 
    try 
         Thread.sleep(ms);
     catch (Exception e) 

【讨论】:

以上是关于睡眠并检查直到条件为真的主要内容,如果未能解决你的问题,请参考以下文章

Qthread - 睡眠直到事件

带有睡眠循环的PHP直到循环结束才更新数据库

当手机进入睡眠状态时,颤振热重载停止工作

IO模型

在开始睡眠(或直到脚本结束)之后对象输出的奇怪延迟

有睡眠/等待吗?