繁体   English   中英

休眠并检查直到条件为真

[英]Sleep and check until condition is true

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 
    }
}

编辑

大多数答案都集中在具有等待和通知或条件的低级 API(它们的工作方式或多或少相同):当您不习惯时,很难做到正确。 证明:其中 2 个答案没有正确使用等待。
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 是线程安全的。

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

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

您应该使用Condition

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

我一直在寻找 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;
        }
    }
}

你不应该睡觉和检查,睡觉和检查。 你想等待一个条件变量并改变条件并在需要做某事时唤醒线程。

我通过使用函数式接口应用这种通用方法解决了这个问题:

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

你可以使用这样的东西,但用你想要检查的条件替换“真”,它会休眠直到它变成真。

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

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

服务员

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM