Czy mogę uzyskać kompletny prosty scenariusz, tj. Samouczek sugerujący, w jaki sposób należy go używać, szczególnie w przypadku kolejki?
wait()
Oraz notify()
sposoby mają na celu zapewnić mechanizm pozwalający gwint do bloku, aż stan specyficzne spełnione. W tym celu zakładam, że chcesz napisać implementację kolejki blokującej, w której masz zapasowy zapas elementów o stałym rozmiarze.
Pierwszą rzeczą, którą musisz zrobić, to określić warunki, na które metody mają czekać. W takim przypadku będziesz chciał, aby put()
metoda blokowała, dopóki nie będzie wolnego miejsca w sklepie, i będziesz chciał, aby take()
metoda blokowała, dopóki jakiś element nie zwróci.
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public synchronized void put(T element) throws InterruptedException {
while(queue.size() == capacity) {
wait();
}
queue.add(element);
notify(); // notifyAll() for multiple producer/consumer threads
}
public synchronized T take() throws InterruptedException {
while(queue.isEmpty()) {
wait();
}
T item = queue.remove();
notify(); // notifyAll() for multiple producer/consumer threads
return item;
}
}
Należy zwrócić uwagę na kilka sposobów korzystania z mechanizmów oczekiwania i powiadamiania.
Po pierwsze, należy upewnić się, że wszelkie wywołania wait()
lub notify()
zsynchronizowane regiony kodu ( wait()
lub notify()
wywołania i są synchronizowane na tym samym obiekcie). Przyczyną tego (poza standardowymi obawami dotyczącymi bezpieczeństwa wątków) jest coś, co nazywane jest brakującym sygnałem.
Przykładem tego jest to, że wątek może wywoływać, put()
gdy kolejka się zapełni, a następnie sprawdza warunek, widzi, że kolejka jest pełna, jednak zanim będzie mogła zablokować inny wątek, zaplanowano. Ten drugi wątek jest wówczas take()
elementem z kolejki i powiadamia oczekujące wątki, że kolejka nie jest już pełna. Ponieważ pierwszy wątek już sprawdził ten warunek, po prostu zadzwoni wait()
po ponownym zaplanowaniu, nawet jeśli może zrobić postęp.
Synchronizując obiekt współdzielony, możesz upewnić się, że ten problem nie wystąpi, ponieważ take()
wywołanie drugiego wątku nie będzie w stanie robić postępu, dopóki pierwszy wątek nie zostanie faktycznie zablokowany.
Po drugie, musisz umieścić warunek sprawdzany w pętli while, a nie instrukcję if, ze względu na problem zwany fałszywymi pobudzeniami. W tym miejscu wątek oczekujący może być czasami ponownie aktywowany bez notify()
wywołania. Umieszczenie tej kontroli w pętli while zapewni, że jeśli wystąpi fałszywe budzenie, warunek zostanie ponownie sprawdzony, a wątek zadzwoni wait()
ponownie.
Jak wspomniano w niektórych innych odpowiedziach, Java 1.5 wprowadziła nową bibliotekę współbieżności (w java.util.concurrent
pakiecie), która została zaprojektowana w celu zapewnienia abstrakcyjnego poziomu wyższego niż mechanizm oczekiwania / powiadamiania. Korzystając z tych nowych funkcji, możesz przepisać oryginalny przykład w następujący sposób:
public class BlockingQueue<T> {
private Queue<T> queue = new LinkedList<T>();
private int capacity;
private Lock lock = new ReentrantLock();
private Condition notFull = lock.newCondition();
private Condition notEmpty = lock.newCondition();
public BlockingQueue(int capacity) {
this.capacity = capacity;
}
public void put(T element) throws InterruptedException {
lock.lock();
try {
while(queue.size() == capacity) {
notFull.await();
}
queue.add(element);
notEmpty.signal();
} finally {
lock.unlock();
}
}
public T take() throws InterruptedException {
lock.lock();
try {
while(queue.isEmpty()) {
notEmpty.await();
}
T item = queue.remove();
notFull.signal();
return item;
} finally {
lock.unlock();
}
}
}
Oczywiście, jeśli rzeczywiście potrzebujesz kolejki blokującej, powinieneś użyć implementacji interfejsu BlockingQueue .
Również w przypadku takich rzeczy bardzo polecam Java Concurrency in Practice , ponieważ obejmuje wszystko, co możesz chcieć wiedzieć o problemach i rozwiązaniach związanych z współbieżnością.
notify
budzi tylko jeden wątek. Jeśli dwa wątki konsumenckie rywalizują o usunięcie elementu, jedno powiadomienie może obudzić drugi wątek konsumencki, co nie może nic z tym zrobić i wróci do trybu uśpienia (zamiast producenta, który, jak mieliśmy nadzieję, wstawi nowy element). Ponieważ wątek producenta nie został przebudzony, nic nie zostało wstawione, a teraz wszystkie trzy wątki będą spały w nieskończoność. Usunąłem mój poprzedni komentarz, ponieważ (niesłusznie) stwierdził, że przyczyną problemu był fałszywyNie przykład kolejki, ale niezwykle prosty :)
Kilka ważnych punktów:
1) NIGDY nie rób
Zawsze używaj while (warunek), ponieważ
while(!pizzaExists){ wait(); }
.2) Musisz przytrzymać blokadę (zsynchronizowaną) przed wywołaniem funkcji wait / nofity. Wątki muszą również uzyskać blokadę przed przebudzeniem.
3) Staraj się unikać uzyskiwania blokady w synchronizowanym bloku i staraj się nie wywoływać metod obcych (metod, których nie wiesz na pewno, co robią). Jeśli musisz, koniecznie podejmij środki, aby uniknąć impasu.
4) Uważaj na powiadomienie (). Pozostań przy powiadomieniuAll (), dopóki nie dowiesz się, co robisz.
5) Na koniec przeczytajcie Współbieżność Java w praktyce !
źródło
pizzaArrived
flagi? jeśli flaga zostanie zmieniona bez wywołania,notify
nie będzie miała żadnego efektu. Również tylko zwait
inotify
wywoła przykład działa.synchronized
słowo kluczowe, deklarowanie zmiennej jest zbędnevolatile
i zaleca się unikanie jej, aby uniknąć pomyłek @mridaNawet jeśli poprosił
wait()
anotify()
konkretnie, czuję, że ten cytat jest nadal ważne, wystarczy:Josh Bloch, Effective Java 2nd Edition , pozycja 69: Preferuj narzędzia do współbieżności
wait
inotify
(podkreślam jego):źródło
notify()
iwait()
ponownieCzy obejrzałeś samouczek Java ?
Ponadto radzę trzymać się z daleka od zabawy tego rodzaju rzeczami w prawdziwym oprogramowaniu. Dobrze się z tym bawić, żebyś wiedział, co to jest, ale współbieżność ma swoje pułapki. Lepiej jest stosować abstrakcje wyższego poziomu i zsynchronizowane kolekcje lub kolejki JMS, jeśli budujesz oprogramowanie dla innych osób.
Tak przynajmniej robię. Nie jestem ekspertem w dziedzinie współbieżności, więc w miarę możliwości unikam ręcznej obsługi wątków.
źródło
Przykład
źródło
Przykład funkcji wait () i replaceall () w wątku.
Zsynchronizowana statyczna lista tablic jest używana jako zasób i wywoływana jest metoda wait (), jeśli lista tablic jest pusta. Metoda powiadomienie () jest wywoływana po dodaniu elementu do listy tablic.
źródło
if(arrayList.size() == 0)
, myślę, że może to być pomyłka.