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

Есть ли библиотека на 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 
 }
}
6 ответов

ИЗМЕНИТЬ

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


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

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


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

wait(). atMost (10, SECONDS).until(() → condition());


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


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


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

официанта

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

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

licensed under cc by-sa 3.0 with attribution.