25

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

这种情况主要发生在测试等待某个条件变为真时。条件受另一个影响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 
    }
}
4

8 回答 8

27

编辑

大多数答案都集中在带有等待和通知或条件的低级 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 是线程安全的。

于 2013-01-22T17:32:35.400 回答
16

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

await().atMost(10, SECONDS).until(() -> condition());
于 2016-10-11T08:00:51.893 回答
8

您应该使用Condition.

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

于 2013-01-22T17:54:41.117 回答
1

我一直在寻找像 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;
        }
    }
}
于 2013-11-28T21:17:18.070 回答
0

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

于 2013-01-22T17:32:19.753 回答
0

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

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) {}
}
于 2021-01-03T10:56:32.227 回答
0

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

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);
    }
}
于 2020-02-12T09:41:22.420 回答
-1

看起来你想要做的是检查条件,然后如果它是假的,等到超时。然后,在另一个线程中,一旦操作完成,就通知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();
 }

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

于 2013-01-22T18:07:08.743 回答