Подтвердить что ты не робот

Сон и проверка, пока не будет выполнено условие

Есть ли библиотека на Java, которая выполняет следующие действия? A thread должен повторно sleep в течение x миллисекунд до тех пор, пока условие не станет истинным или не будет достигнуто максимальное время.

Эта ситуация чаще всего возникает, когда тест ждет, пока какое-то условие станет истинным. На состояние влияет другое thread.

[EDIT] Просто, чтобы сделать его более ясным, я хочу, чтобы тест ожидал только 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 
    }
}
4b9b3361

Ответ 1

ИЗМЕНИТЬ

В большинстве ответов основное внимание уделяется API низкого уровня с ожиданием и уведомлением или Условиями (которые работают более или менее одинаково): трудно получить право, если вы к нему не привыкли. Доказательство. 2 из этих ответов не работают правильно. java.util.concurrent предлагает вам API высокого уровня, где все эти сложности скрыты.

IMHO, нет смысла использовать шаблон wait/notify, когда есть встроенный класс в параллельном пакете, который достигает того же самого.


A CountDownLatch с начальным счетом 1 делает именно это:

  • Когда условие станет истинным, вызовите 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 являются потокобезопасными.

Ответ 2

Awaitility предлагает простое и понятное решение:

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

Ответ 3

Вы должны использовать Condition.

Если вы хотите иметь тайм-аут в дополнение к условию, см. await(long time, TimeUnit unit)

Ответ 4

Я искал решение, подобное тому, что предлагает 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;
        }
    }
}

Ответ 5

Вам не следует спать и проверять, спать и проверять. Вы хотите подождать переменную условия и изменить условие и пробудить поток, когда пришло время что-то сделать.

Ответ 6

Кажется, что вы хотите проверить это условие, а затем, если оно ложно, подождите до таймаута. Затем, в другом потоке, уведомите об этом после завершения операции.

официанта

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

Changer

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

Это должно сделать трюк для вас. Вы также можете сделать это, используя блокировку вращения, но вам нужно будет проверять тайм-аут каждый раз, когда вы проходите цикл. Однако код может быть немного проще.