【问题标题】: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 的问题。

标签: java multithreading sleep


【解决方案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) {}
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-01-06
            • 1970-01-01
            • 1970-01-01
            • 2011-10-28
            相关资源
            最近更新 更多