Why must wait () always be in synchronized block
Wszyscy wiemy, że aby przywołać Object.wait()
, to wywołanie musi być umieszczone w zsynchronizowanym bloku, w przeciwnym razie IllegalMonitorStateException
jest wyrzucony. Ale jaki jest powód wprowadzenia tego ograniczenia? wiem, że wait()
zwalnia monitor, ale dlaczego musimy jawnie nabyć monitor poprzez zsynchronizowanie określonego bloku, a następnie zwolnić monitor przez wywołanie wait()
?
Jakie jest potencjalne uszkodzenie, jeśli możliwe było wywołanie wait()
poza zsynchronizowany blok, zachowując jego semantykę-zawieszenie wątku rozmówcy?
10 answers
A wait()
ma sens tylko wtedy, gdy istnieje również notify()
, więc zawsze chodzi o komunikację między wątkami, a to wymaga synchronizacji, aby działać poprawnie. Można argumentować, że powinno to być niejawne, ale to naprawdę nie pomoże, z następującego powodu: {]}
Semantycznie, nigdy tylko wait()
. Potrzebujesz jakiegoś warunku, aby być usatysfakcjonowanym, a jeśli nie, poczekaj, aż tak będzie. Więc to, co naprawdę robisz, to
if(!condition){
wait();
}
Ale warunek jest ustawiany przez osobny wątek, więc w aby to działało poprawnie, potrzebujesz synchronizacji.
Jeszcze kilka rzeczy nie tak z tym, gdzie to, że Twój wątek przestał czekać, nie oznacza, że warunek, którego szukasz, jest prawdziwy:
-
Możesz uzyskać fałszywe przebudzenia (co oznacza, że wątek może obudzić się z czekania bez otrzymania powiadomienia) lub
Warunek może zostać ustawiony, ale trzeci wątek powoduje, że warunek jest fałszywy, zanim wątek oczekujący się obudzi (i odzyskuje monitor).
Aby poradzić sobie z tymi przypadkami, naprawdę potrzebujesz zawsze jakiejś odmiany tego:
synchronized(lock){
while(!condition){
lock.wait();
}
}
Jeszcze lepiej, nie zadzieraj z prymitywami synchronizacji w ogóle i pracuj z abstrakcjami oferowanymi w pakietach java.util.concurrent
.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-07 18:03:12
Jakie jest potencjalne uszkodzenie, jeśli możliwe było wywołanie
wait()
poza zsynchronizowanym blokiem, zachowując jego semantykę-zawieszając wątek wywołujący?
Zilustrujmy jakie problemy napotkalibyśmy, gdyby wait()
można było wywołać poza zsynchronizowanym blokiem za pomocąkonkretnego przykładu .
Załóżmy, że mamy zaimplementować kolejkę blokującą (wiem, jest już taka w API :)
Pierwsza próba (bez synchronizacja) może wyglądać coś w linii poniżej
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
Oto, co może się potencjalnie wydarzyć:
Wątek konsumencki wywołuje
take()
i widzi, żebuffer.isEmpty()
.Zanim wątek konsumencki zadzwoni
wait()
, pojawia się wątek producenta i wywołuje pełnegive()
, czyli,buffer.add(data); notify();
Wątek konsumencki będzie teraz wywoływał
wait()
(i missnotify()
, który właśnie został wywołany).Jeśli niestety wątek producenta nie wyprodukuje więcej
give()
, ponieważ wątek konsumencki nigdy się nie budzi, a my mamy martwy zamek.
Gdy zrozumiesz Problem, Rozwiązanie jest oczywiste: użyj synchronized
, aby upewnić się, że notify
nigdy nie zostanie wywołana pomiędzy isEmpty
a wait
.
Bez wchodzenia w szczegóły: ten problem z synchronizacją jest uniwersalny. Jak wskazuje Michael Borgwardt, wait / notify polega na komunikacji między wątkami, więc zawsze skończysz z stan rasy podobny do opisanego powyżej. Z tego powodu obowiązuje zasada "only wait inside synchronized".
Akapit z linku zamieszczonego przez @ Willie podsumowuje to całkiem dobrze:
Potrzebujesz absolutnej gwarancji, że kelner i zgłaszający zgadzają się co do stanu orzeczenia. Kelner sprawdza stan predykatu w pewnym momencie nieznacznie, zanim przejdzie w stan uśpienia, ale poprawność zależy od tego, czy predykat jest prawdziwy Kiedy idzie spać. Pomiędzy tymi dwoma zdarzeniami jest okres podatności, który może złamać program.
Orzeczenie, które producent i konsument muszą uzgodnić, znajduje się w powyższym przykładzie buffer.isEmpty()
. Umowa zostaje rozwiązana poprzez zapewnienie, że operacje wait I notify są wykonywane w blokach synchronized
.
Ten post został przepisany jako artykuł tutaj: Java: Why wait must be called in a synchronized block
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-11-25 14:32:35
@Rollerball ma rację. Wywołanie wait()
jest wywołane tak, że wątek może czekać na jakiś warunek, gdy to wywołanie wait()
się stanie, wątek jest zmuszony zrezygnować z blokady.
Aby z czegoś zrezygnować, najpierw musisz to posiadać. Wątek musi najpierw posiadać zamek.
Stąd potrzeba wywołania go wewnątrz synchronized
metoda / blok.
Tak, zgadzam się ze wszystkimi powyższymi odpowiedziami dotyczącymi potencjalnych uszkodzeń / niespójności, jeśli nie sprawdziłeś warunku w synchronized
metoda / blok. Jednak jako @shrini1000 zwrócił uwagę, samo wywołanie wait()
w bloku synchronizowanym nie zapobiegnie tej niespójności.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-08-21 07:17:42
Problem może spowodować, jeśli nie synchronizować przed wait()
jest następujący:
- jeśli pierwszy wątek przechodzi do
makeChangeOnX()
i sprawdza warunek while I jesttrue
(x.metCondition()
zwracafalse
, czylix.condition
jestfalse
), więc dostanie się do środka. Następnie, tuż przed metodąwait()
, kolejny wątek przechodzi dosetConditionToTrue()
i ustawiax.condition
natrue
inotifyAll()
. - wtedy dopiero po tym, 1. Wątek wejdzie w jego metodę
wait()
(bez wpływu nanotifyAll()
, które zdarzyło się kilka chwilę wcześniej). W tym przypadku pierwszy wątek będzie czekał na kolejny wątek do wykonaniasetConditionToTrue()
, ale to może się nie powtórzyć.
Ale jeśli umieścisz
synchronized
przed metodami, które zmieniają stan obiektu, to tak się nie stanie.
class A {
private Object X;
makeChangeOnX(){
while (! x.getCondition()){
wait();
}
// Do the change
}
setConditionToTrue(){
x.condition = true;
notifyAll();
}
setConditionToFalse(){
x.condition = false;
notifyAll();
}
bool getCondition(){
return x.condition;
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-17 15:15:00
Wszyscy wiemy, że metody wait (), notify() i notifyAll() są używane dla inter-threaded komunikacja. Aby pozbyć się nieodebranych sygnałów i fałszywych problemów z przebudzeniem, wątek oczekiwania zawsze czeka na pewne warunki. np. -
boolean wasNotified = false;
while(!wasNotified) {
wait();
}
Wtedy notifying thread sets wasNotified variable to true and notify.
Każdy wątek ma swoją lokalną pamięć podręczną, więc wszystkie zmiany są najpierw tam zapisywane i następnie stopniowo awansował do pamięci głównej.
Gdyby te metody nie były wywoływane w blok zsynchronizowany, zmienna nie została nie zostanie spłukana do pamięci głównej i będzie tam w lokalnej pamięci podręcznej wątku więc wątek oczekujący będzie czekał na sygnał, chociaż został zresetowany przez powiadomienie nić.
Aby rozwiązać tego typu problemy, metody te są zawsze wywoływane wewnątrz zsynchronizowanego bloku co zapewnia, że po uruchomieniu synchronizowanego bloku wszystko będzie odczytywane z głównego pamięci i zostaną spłukane do pamięci głównej przed zakończeniem zsynchronizowanego blok.
synchronized(monitor) {
boolean wasNotified = false;
while(!wasNotified) {
wait();
}
}
Dzięki, mam nadzieję, że to wyjaśni.Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-01 16:47:58
Ma to zasadniczo związek z architekturą sprzętową (tj. RAM i pamięci podręczne ).
Jeśli nie używasz synchronized
razem z wait()
lub notify()
, inny wątek Może wprowadzić ten sam blok, zamiast czekać, aż monitor go wprowadzi. Co więcej, gdy np. uzyskuje dostęp do tablicy bez zsynchronizowanego bloku, inny wątek może nie widzieć zmiany do niej...właściwie inny wątek nie będzie widział żadnych zmian w nim Gdy ma już Kopia tablicy w buforze X-level (Alias 1st/2nd/3rd-level cache) wątku obsługującego rdzeń CPU.
Ale zsynchronizowane bloki to tylko jedna strona medalu: jeśli rzeczywiście uzyskasz dostęp do obiektu w zsynchronizowanym kontekście z niezsynchronizowanego kontekstu, obiekt nadal nie będzie zsynchronizowany nawet w zsynchronizowanym bloku, ponieważ posiada własną kopię obiektu w swoim buforze. Pisałem o tym tutaj: https://stackoverflow.com/a/21462631 I Gdy a lock przechowuje niekończący się obiekt, czy odniesienie do obiektu może być jeszcze zmienione przez inny wątek?
Ponadto jestem przekonany, że pamięci podręczne poziomu x są odpowiedzialne za większość nie odtwarzalnych błędów wykonawczych. To dlatego, że programiści zwykle nie uczą się rzeczy niskiego poziomu, jak na przykład jak działa procesor lub jak hierarchia pamięci wpływa na działanie aplikacji: {24]} http://en.wikipedia.org/wiki/Memory_hierarchy
Pozostaje zagadką dlaczego klasy programowania nie zacznij od hierarchii pamięci i architektury procesora. "Hello world" tu nie pomoże. ;)
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 12:26:38
zgodnie z docs:
Bieżący wątek musi posiadać monitor tego obiektu. Thread releases własność tego monitora.
wait()
metoda oznacza po prostu, że zwalnia blokadę na obiekcie. Tak więc obiekt zostanie zablokowany tylko w obrębie zsynchronizowanego bloku/metody. Jeśli wątek znajduje się poza blokiem synchronizacji, oznacza to, że nie jest zablokowany, jeśli nie jest zablokowany, to co wydałbyś na obiekcie?
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-06-03 07:29:34
Bezpośrednio z tego java Oracle tutorial:
Gdy wątek wywołuje d.wait, musi posiadać wewnętrzny zamek dla d - w przeciwnym razie zostanie wyrzucony błąd. Wywołanie oczekiwania wewnątrz zsynchronizowanego metoda jest prostym sposobem uzyskania wewnętrznego zamka.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-27 07:30:47
Gdy wywołujesz notify() z obiektu T, java powiadamia o tym konkretną metodę t.wait (). Ale w jaki sposób java wyszukuje i powiadamia określoną metodę oczekiwania.
Java sprawdza tylko zsynchronizowany blok kodu, który został zablokowany przez obiekt t. java nie może przeszukiwać całego kodu, aby powiadomić konkretną t.wait ().
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-10 08:23:53
Thread wait na obiekcie monitorującym (obiekcie używanym przez blok synchronizacji), może być n liczba obiektów monitorujących w całej podróży pojedynczego wątku. Jeśli wątek czeka poza blokiem synchronizacji, to nie ma obiektu monitorowania, a także innego wątku powiadamiającego o dostępie do obiektu monitorowania, więc w jaki sposób wątek poza blokiem synchronizacji wiedziałby, że został powiadomiony. Jest to również jeden z powodów, dla których wait (), notify() i notifyAll() są w Klasa obiektu, a nie klasa wątku.
Zasadniczo obiekt monitorowania jest wspólnym zasobem dla wszystkich wątków, a obiekty monitorowania mogą być dostępne tylko w bloku synchronizacji.
class A {
int a = 0;
//something......
public void add() {
synchronization(this) {
//this is your monitoring object and thread has to wait to gain lock on **this**
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2020-06-21 18:16:49