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

Ожидание события на Java - насколько это сложно?

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

Thread 1:
    while(true) {
        ...do something...
        foo.notifyAll()
        ...wait for some condition that might never happen...
        ...
    }

Thread 2:
    ...
    foo.wait();
    ...

Теперь это выглядит хорошо и все, если Thread 1 notifyAll() не запускается до Thread 2 wait(), и в этом случае Thread 2 ждет, пока Thread 1 не уведомит об этом (что может никогда не произойти).

Мои возможные решения:

a) Я мог бы использовать CountDownLatch или Будущее, но у обоих есть проблема, по которой они по сути работают только один раз. То есть, в Thread 1 while loop, мне нужно будет создать новый foo, чтобы ждать каждый раз, и Thread 2 должен будет спросить, какого foo ждать. У меня плохое представление о простом написании

while(true) {
   foo = new FutureTask(); 
   ...
   foo.set(...);
   ...wait for a condition that might never be set...
   ...
}

поскольку я боюсь, что при foo = new FutureTask(), что происходит, когда кто-то ждал старого foo (по какой-то причине, set не был вызван, например, ошибка в обработке исключений)?

b) Или я мог бы использовать семафор:

class Event {
   Semaphore sem;
   Event() { sem = new Semaphore(1); sem . }
   void signal() { sem.release(); }
   void reset() { sem.acquire(1); }
   void wait() { if (sem.tryAcquire(1)) { sem.release(); } }
}

Но я боюсь, что есть какое-то условие гонки, если несколько потоков ждут() для него, а еще один сигнал() s и reset() s.

Вопрос:

Нет ли в Java API ничего похожего на поведение Windows Event? Или, если вы презираете Windows, что-то вроде golang WaitGroup (т.е. CountDownLatch, который позволяет countUp())? Что-нибудь?

Как это сделать вручную:

Thread 2 не может просто ждать из-за ложного пробуждения и в Java нет способа узнать, почему Object.wait() возвращен. Поэтому мне нужна переменная условия, которая хранит сообщение о событии или нет. Тема 2:

synchronized(foo) {
    while(!condition) {
        foo.wait();
    }
}

И Thread 1, конечно, устанавливает условие true в синхронизированном блоке. Благодаря недельным подсказкам!

Существует ли существующий класс, который обертывает это поведение?

Или мне нужно скопировать и вставить код целиком?

4b9b3361

Ответ 1

Стандартная практика заключается в том, чтобы изменить какое-то состояние при выполнении notifyAll и проверить некоторое состояние при выполнении wait().

например.

boolean ready = false;

// thread 1
synchronized(lock) {
    ready = true;
    lock.notifyAll();
}


// thread 2
synchronized(lock) {
    while(!ready) 
        lock.wait();
}

При таком подходе не имеет значения, сначала запустит ли поток 1 или нить 2 блокировку.

Некоторые инструменты анализа кода выдадут вам предупреждение, если вы используете уведомление или подождите, не устанавливая значение или не проверяя значение.

Ответ 2

Вы можете использовать wait() с таймаутом, и в этом случае вы не рискуете ждать вечно. Также обратите внимание, что wait() может вернуться, даже если вообще не было уведомлений(), поэтому вам нужно будет обернуть ваше ожидание внутри некоторого условного цикла. Это стандартный способ ожидания на Java.

synchronized(syncObject) {
    while(condition.isTrue()) {
        syncObject.wait(WAIT_TIMEOUT);
    }
}

(в вашей теме 2)

Изменить: Перемещено синхронизировано вне цикла.

Ответ 3

Самый простой способ - просто сказать

firstThread.join();

Это будет блокироваться до тех пор, пока первый поток не будет завершен.

Но вы можете реализовать то же самое с помощью wait/notify. К сожалению, вы не разместили свои реальные фрагменты кода, но я думаю, что если вы не выходите, когда вы вызываете уведомление, это происходит, потому что вы не помещали оба в блок synchronized. Обратите внимание, что "аргумент" блока synchronized должен быть одинаковым для пары wait/notify.

Ответ 4

Я использовал BlockingQueue между двумя потоками. Использовать wait и notify так 5 минут назад;)

enum Event {
  Event,
  Stop;
}

BlockingQueue<Event> queue = new LinkedBlockingQueue<Event>();

// Thread 1
try {
  while(true) {
    ...do something...
    queue.put(Event.Event);
    ...wait for some condition that might never happen...
    ...
  }
} finally {
  // Tell other thread we've finished.
  queue.put(Event.Stop};
}

// Thread 2
...
switch ( queue.take() ) {
  case Event:
       ...
       break;

  default:
       ...
       break;
}