Jakie przypadki wymagają synchronizowanego dostępu do metod w Javie?
W jakich przypadkach konieczna jest synchronizacja dostępu do członków instancji? Rozumiem, że dostęp do statycznych członków klasy zawsze musi być zsynchronizowany-ponieważ są one współdzielone przez wszystkie instancje obiektu klasy.
Moje pytanie brzmi, Kiedy będę niepoprawny, jeśli nie zsynchronizuję członków instancji?
Na przykład, jeśli moja klasa jest
public class MyClass {
private int instanceVar = 0;
public setInstanceVar()
{
instanceVar++;
}
public getInstanceVar()
{
return instanceVar;
}
}
W jakich przypadkach (użycia klasy MyClass
) chciałbym potrzebować aby mieć metody:
public synchronized setInstanceVar()
oraz
public synchronized getInstanceVar()
?
Z góry dzięki za odpowiedzi.
6 answers
To zależy od tego, czy chcesz, aby Twoja klasa była bezpieczna. Większość klas nie powinna być bezpieczna dla wątków (dla uproszczenia), w tym przypadku nie potrzebujesz synchronizacji. Jeśli chcesz, aby była bezpieczna dla wątków, powinieneś zsynchronizować access lub, aby zmienna była zmienna zmienna. (Unika innych wątków uzyskiwania "starych" danych.)
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
2008-11-21 18:01:27
Modyfikator synchronized
jest naprawdę złym pomysłem i należy go unikać za wszelką cenę. Myślę, że to godne pochwały, że Sun starał się, aby blokowanie trochę łatwiej osiągnąć, ale synchronized
po prostu powoduje więcej kłopotów niż jest warte.
Problem polega na tym, że metoda synchronized
jest w rzeczywistości cukrem składniowym służącym do uzyskania blokady this
i przytrzymania jej przez czas trwania metody. Tak więc, public synchronized void setInstanceVar()
byłoby równoważne coś takiego:
public void setInstanceVar() {
synchronized(this) {
instanceVar++;
}
}
To jest złe dla dwóch uzasadnienie:
- Wszystkie metody
synchronized
w tej samej klasie używają dokładnie tej samej blokady, co zmniejsza przepustowość - dostęp do zamka może uzyskać każdy, także członkowie innych klas.
Nic mnie nie powstrzyma przed zrobieniem czegoś takiego w innej klasie:
MyClass c = new MyClass();
synchronized(c) {
...
}
Wewnątrz tego bloku synchronized
trzymam blokadę wymaganą przez wszystkie metody synchronized
wewnątrz MyClass
. To dodatkowo zmniejsza przepustowość i dramatycznie zwiększa szanse na impas.
Lepszym podejściem jest posiadanie dedykowanego obiektu lock
i użycie bloku synchronized(...)
bezpośrednio:
public class MyClass {
private int instanceVar;
private final Object lock = new Object(); // must be final!
public void setInstanceVar() {
synchronized(lock) {
instanceVar++;
}
}
}
Alternatywnie można użyć interfejsu java.util.concurrent.Lock
i implementacji java.util.concurrent.locks.ReentrantLock
, aby osiągnąć zasadniczo ten sam wynik (w rzeczywistości jest to takie samo w Javie 6).
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
2008-11-21 18:18:50
Jeśli chcesz, aby ten wątek klasy był bezpieczny, zadeklarowałbym instanceVar
jako volatile
, aby upewnić się, że otrzymujesz zawsze najbardziej aktualną wartość z pamięci, a także zrobiłbym setInstanceVar()
synchronized
ponieważ w JVM przyrost nie jest operacją atomową.
private volatile int instanceVar =0;
public synchronized setInstanceVar() { instanceVar++;
}
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
2008-11-21 18:08:27
. Z grubsza odpowiedź brzmi: "to zależy". Synchronizacja settera i gettera tutaj miałaby tylko zamierzony cel zagwarantowania, że wiele wątków nie będzie w stanie odczytać zmiennych między sobą:
synchronized increment()
{
i++
}
synchronized get()
{
return i;
}
Ale to naprawdę nie działa tutaj, ponieważ aby zapewnić, że Twój wątek rozmówcy ma tę samą wartość, którą zwiększa, musisz zagwarantować, że atomicznie zwiększasz, a następnie odzyskujesz, czego nie robisz tutaj-tj. coś jak
synchronized int {
increment
return get()
}
Zasadniczo synchronizacja jest przydatna do określenia, które operacje muszą być zagwarantowane, aby uruchomić threadsafe (inotherwords, nie możesz stworzyć sytuacji, w której oddzielny wątek podważa Twoją operację i sprawia, że twoja klasa zachowuje się nielogicznie lub podważa oczekiwany stan danych). To w rzeczywistości większy temat, niż można tutaj poruszyć.
Ta książka współbieżność Javy w praktyce jest doskonała, a na pewno znacznie więcej wiarygodny ode mnie.
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
2008-11-21 18:15:10
Mówiąc najprościej, używasz synchronized, gdy masz wątki mutliple uzyskujące dostęp do tej samej metody tej samej instancji, która zmieni stan obiektu / lub aplikacji.
Jest to prosty sposób, aby zapobiec Warunkom wyścigu między wątkami, a tak naprawdę powinieneś go używać tylko wtedy, gdy planujesz mieć współbieżne wątki uzyskujące dostęp do tej samej instancji, takiej jak obiekt globalny.
Teraz, gdy czytasz stan instancji obiektu z równoczesnym wątki, warto zajrzeć do Javy.util./ align = "left" / zamki.ReentrantReadWriteLock -- który teoretycznie pozwala na odczyt wielu wątków jednocześnie, ale tylko jeden wątek może pisać. Tak więc w przykładzie metody getter i setting, który wszyscy wydają się dawać, możesz wykonać następujące czynności:
public class MyClass{
private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
private int myValue = 0;
public void setValue(){
rwl.writeLock().lock();
myValue++;
rwl.writeLock().unlock();
}
public int getValue(){
rwl.readLock.lock();
int result = myValue;
rwl.readLock.unlock();
return result;
}
}
W Javie operacje na ints są atomowe, więc nie, w tym przypadku nie musisz synchronizować, jeśli robisz tylko 1 zapis i 1 odczyt na raz.
Jeśli były to długie lub podwójne, musisz zsynchronizować, ponieważ jest możliwe, aby część long/double została zaktualizowana, a następnie odczytać inny wątek, a następnie w końcu druga część long / double została zaktualizowana.
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
2008-11-21 18:04:32