Сон и проверка, пока не будет выполнено условие
Есть ли библиотека на 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
}
}
Ответы
Ответ 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();
}
Это должно сделать трюк для вас. Вы также можете сделать это, используя блокировку вращения, но вам нужно будет проверять тайм-аут каждый раз, когда вы проходите цикл. Однако код может быть немного проще.