睡觉并检查,直到情况成立

时间:2013-01-22 17:30:18

标签: java multithreading sleep

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

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

[编辑]为了让它更清楚,我希望测试在失败前只等待X ms。它不能永远等待条件成为现实。我正在添加一个人为的例子。

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

8 个答案:

答案 0 :(得分:19)

修改

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

恕我直言,当并发包中有一个实现相同的内置类时,没有必要使用wait / notify模式。


初始计数为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是线程安全的。

答案 1 :(得分:8)

您应该使用Condition

如果您希望除条件外还有超时,请参阅await(long time, TimeUnit unit)

答案 2 :(得分:7)

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

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

答案 3 :(得分: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;
        }
    }
}

答案 4 :(得分:0)

你不应该睡觉,检查,睡觉和检查。您希望等待一个条件变量并让条件发生变化,并在执行某些操作时唤醒该线程。

答案 5 :(得分: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);
    }
}

答案 6 :(得分:0)

您可以使用类似的方法,但将“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) {}
}

答案 7 :(得分:-1)

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

那应该为你做的伎俩。您也可以使用自旋锁执行此操作,但每次执行循环时都需要检查超时。实际上代码可能实际上有点简单。