java.util.concurrent
Interfejs API zapewnia klasę o nazwie as Lock
, która w zasadzie serializowałaby kontrolę w celu uzyskania dostępu do zasobu krytycznego. Daje metodę taką jak park()
i unpark()
.
Możemy zrobić podobne rzeczy, jeśli możemy użyć synchronized
słowa kluczowego oraz użycia wait()
i notify() notifyAll()
metod.
Zastanawiam się, który z nich jest lepszy w praktyce i dlaczego?
Odpowiedzi:
Jeśli po prostu blokujesz obiekt, wolałbym użyć
synchronized
Przykład:
Musisz wyraźnie robić
try{} finally{}
wszędzie.Podczas gdy zsynchronizowane, jest bardzo jasne i niemożliwe do popełnienia błędu:
To powiedziawszy,
Lock
mogą być bardziej przydatne w przypadku bardziej skomplikowanych rzeczy, w których nie można uzyskać i zwolnić w tak czysty sposób. Szczerze mówiąc wolałbymLock
przede wszystkim unikać korzystania z go, i po prostu zastosować bardziej wyrafinowaną kontrolę współbieżności, taką jak aCyclicBarrier
lub aLinkedBlockingQueue
, jeśli spełniają one Twoje potrzeby.Nigdy nie miałem powód do używania
wait()
albonotify()
ale niektóre z nich mogą być dobre.źródło
std::lock_guard
Odkryłem, że
Lock
iCondition
(i inne noweconcurrent
klasy) to po prostu więcej narzędzi do zestawu narzędzi. Mógłbym zrobić wszystko, czego potrzebowałem, za pomocą mojego starego młota pazurowego (synchronized
słowo kluczowe), ale w niektórych sytuacjach korzystanie z niego było niewygodne. Kilka z tych niezręcznych sytuacji stało się o wiele prostszych, gdy dodałem więcej narzędzi do mojego zestawu narzędzi: gumowy młotek, młot kulkowy, prybar i kilka ciosów w gwoździe. Jednak mój stary młot pazur nadal widzi swój udział w użyciu.Nie sądzę, aby jedno było naprawdę „lepsze” niż drugie, ale raczej każde lepiej pasuje do różnych problemów. W skrócie, prosty model i charakter zorientowany na zakres
synchronized
pomaga chronić mnie przed błędami w moim kodzie, ale te same zalety są czasem przeszkodami w bardziej złożonych scenariuszach. To są te bardziej złożone scenariusze, że pakiet pomocniczy został stworzony, aby pomóc rozwiązać. Ale korzystanie z tego konstruktu wyższego poziomu wymaga bardziej wyraźnego i ostrożnego zarządzania kodem.===
Myślę, że JavaDoc dobrze sobie radzi z opisywaniem rozróżnienia między
Lock
isynchronized
(nacisk jest mój):źródło
Możesz osiągnąć wszystko to, co media w java.util.concurrent czynienia z prymitywów niskopoziomowych jak
synchronized
,volatile
albo czekać / zawiadomićJednak współbieżność jest trudna i większość ludzi popełnia co najmniej niektóre jej błędy, co powoduje, że ich kod jest niepoprawny lub nieefektywny (lub oba).
Współbieżny interfejs API zapewnia podejście wyższego poziomu, które jest łatwiejsze (i jako takie bezpieczniejsze) w użyciu. W skrócie, nie powinieneś już używać
synchronized, volatile, wait, notify
bezpośrednio.Blokada klasa sama w sobie jest po stronie niższego poziomu tego zestawu narzędzi, można nawet nie trzeba używać bezpośrednio albo (można użyć
Queues
i Semaphore i rzeczy, etc, większość czasu).źródło
java.util.concurrent
jest łatwiejsze [ogólnie] niż funkcje językowe (synchronized
itp.). Kiedy używaszjava.util.concurrent
, musisz przyzwyczaić się do wypełnianialock.lock(); try { ... } finally { lock.unlock() }
przed napisaniem kodu, podczas gdy przy pomocy wsynchronized
zasadzie wszystko jest w porządku od samego początku. Na tej podstawie powiedziałbym, żesynchronized
jest łatwiejsze (biorąc pod uwagę, że chcesz jego zachowania) niżjava.util.concurrent.locks.Lock
. par 4Istnieją 4 główne czynniki, dla których chcesz użyć
synchronized
lubjava.util.concurrent.Lock
.Uwaga: synchroniczne blokowanie jest tym, co mam na myśli, gdy mówię o zamknięciu wewnętrznym.
Kiedy Java 5 pojawiła się z ReentrantLocks, okazało się, że mają dość zauważalną różnicę w przepustowości niż wewnętrzne blokowanie. Jeśli szukasz szybszego mechanizmu blokującego i korzystasz z wersji 1.5, rozważ jucReentrantLock. Wewnętrzne blokowanie Java 6 jest teraz porównywalne.
jucLock ma różne mechanizmy blokowania. Blokuj przerywalne - próbuj zablokować, dopóki nitka blokująca nie zostanie przerwana; blokada czasowa - spróbuj zablokować na określony czas i poddaj się, jeśli ci się nie uda; tryLock - spróbuj zablokować, jeśli jakiś inny wątek trzyma blokadę, zrezygnuj. Wszystko to jest zawarte oprócz prostej blokady. Blokowanie samoistne zapewnia jedynie proste blokowanie
źródło
Chciałbym dodać jeszcze kilka rzeczy do odpowiedzi Berta F.
Locks
obsługują różne metody dokładniejszego sterowania zamkiem, które są bardziej ekspresyjne niż niejawne monitory (synchronized
zamki)Zalety Lock over Synchronization od strony dokumentacji
Zastosowanie zsynchronizowanych metod lub instrukcji zapewnia dostęp do niejawnej blokady monitora związanej z każdym obiektem, ale zmusza do uzyskania i zwolnienia wszystkich blokad w strukturze blokowej
Implementacje blokady zapewniają dodatkową funkcjonalność w stosunku do stosowania zsynchronizowanych metod i instrukcji, zapewniając nieblokującą próbę uzyskania blokady
lock (tryLock())
, próbę uzyskania blokady, którą można przerwać (lockInterruptibly()
oraz próbę uzyskania blokady, która możetimeout (tryLock(long, TimeUnit))
.Klasa Lock może również zapewniać zachowanie i semantykę, które są zupełnie inne niż niejawna blokada monitora, takie jak gwarantowane porządkowanie, użycie bez ponownego wysłania lub wykrycie zakleszczenia
ReentrantLock : Mówiąc prosto, zgodnie z moim rozumieniem,
ReentrantLock
pozwala obiektowi ponownie wejść z jednej sekcji krytycznej do innej sekcji krytycznej. Ponieważ masz już blokadę umożliwiającą wejście do jednej sekcji krytycznej, możesz użyć innej sekcji krytycznej na tym samym obiekcie, używając bieżącej blokady.ReentrantLock
kluczowe funkcje zgodnie z tym artykułemMożesz użyć
ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
do dalszego uzyskania kontroli nad szczegółowym blokowaniem operacji odczytu i zapisu.Oprócz tych trzech ReentrantLocks java 8 zapewnia jeszcze jedną blokadę
StampedLock:
Możesz użyć tych znaczków, aby zwolnić blokadę lub sprawdzić, czy blokada jest nadal ważna. Dodatkowo wybite zamki obsługują inny tryb blokady zwany blokowaniem optymistycznym.
Zapoznaj się z tym artykułem na temat używania różnych rodzajów
ReentrantLock
iStampedLock
zamków.źródło
Główną różnicą jest sprawiedliwość, innymi słowy, czy prośby są rozpatrywane w FIFO, czy może istnieć barka? Synchronizacja na poziomie metody zapewnia sprawiedliwy lub FIFO podział zamka. Za pomocą
lub
nie zapewnia uczciwości.
Jeśli masz spory o blokadę, możesz łatwo natknąć się na barki, w których nowsze żądania blokują się, a starsze blokują się. Widziałem przypadki, w których 200 wątków przychodzi w krótkiej kolejności do blokady, a drugi, który przybywa, jest przetwarzany jako ostatni. Jest to odpowiednie dla niektórych aplikacji, ale dla innych jest zabójcze.
Pełna książka na ten temat znajduje się w książce Briana Goetza „Java Concurrency In Practice”, rozdział 13.3.
źródło
Książka Briana Goetza „Java Concurrency In Practice”, sekcja 13.3: „... Podobnie jak domyślna ReentrantLock, blokowanie wewnętrzne nie oferuje żadnych deterministycznych gwarancji uczciwości, ale gwarancje uczciwości statystycznej większości implementacji blokowania są wystarczające dla prawie wszystkich sytuacji ...”
źródło
Blokada ułatwia życie programistom. Oto kilka sytuacji, które można łatwo osiągnąć za pomocą zamka.
Natomiast blokada i warunki są oparte na zsynchronizowanym mechanizmie. Dlatego z pewnością można osiągnąć tę samą funkcjonalność, co przy użyciu zamka. Jednak rozwiązywanie złożonych scenariuszy zsynchronizowanych może utrudnić ci życie i może odejść od rozwiązania rzeczywistego problemu.
źródło
Główna różnica między blokadą a synchronizacją:
źródło
Blokowanie i synchronizowanie bloku służy temu samemu celowi, ale zależy to od użycia. Rozważ poniższą część
W powyższym przypadku, jeśli wątek wejdzie do bloku synchronizacji, drugi blok również zostanie zablokowany. Jeśli istnieje wiele takich bloków synchronizacji na tym samym obiekcie, wszystkie bloki są zablokowane. W takich sytuacjach można użyć java.util.concurrent.Lock, aby zapobiec niepożądanemu blokowaniu bloków
źródło