Za każdym razem, gdy pojawia się pytanie na temat SO dotyczące synchronizacji Java, niektórzy bardzo chętnie wskazują, że synchronized(this)
należy tego unikać. Zamiast tego twierdzą, że preferowana jest blokada prywatnego odwołania.
Niektóre z podanych powodów to:
- jakiś zły kod może ukraść twoją blokadę (bardzo popularny ten, ma również wariant „przypadkowo”)
- wszystkie zsynchronizowane metody w tej samej klasie używają dokładnie tej samej blokady, co zmniejsza przepustowość
- (niepotrzebnie) ujawniasz zbyt wiele informacji
Inni ludzie, w tym ja, twierdzą, że synchronized(this)
jest to często używany idiom (także w bibliotekach Java), bezpieczny i dobrze rozumiany. Nie należy tego unikać, ponieważ masz błąd i nie masz pojęcia, co dzieje się w twoim programie wielowątkowym. Innymi słowy: jeśli ma to zastosowanie, użyj go.
Chciałbym zobaczyć przykłady z prawdziwego świata (bez fobarów), w których this
lepiej unikać unikania blokady, gdybym synchronized(this)
również wykonał zadanie.
Dlatego: czy zawsze należy go unikać synchronized(this)
i zastępować zamkiem prywatnego odniesienia?
Kilka dodatkowych informacji (zaktualizowanych po udzieleniu odpowiedzi):
- mówimy o synchronizacji instancji
- brane są pod uwagę zarówno domniemane (
synchronized
metody), jak i jawna formasynchronized(this)
- jeśli cytujesz Blocha lub inne autorytety na ten temat, nie pomijaj części, które ci się nie podobają (np. Efektywna Java, element dotyczący bezpieczeństwa wątków: Zazwyczaj jest to blokada samej instancji, ale są wyjątki).
- jeśli potrzebujesz szczegółowości w blokowaniu innym niż
synchronized(this)
zapewnia,synchronized(this)
to nie dotyczy, więc nie o to chodzi
Odpowiedzi:
Omówię każdy punkt osobno.
Bardziej martwię się o przypadek . Sprowadza się to do tego, że takie użycie
this
jest częścią ujawnionego interfejsu twojej klasy i powinno być udokumentowane. Czasami pożądana jest zdolność innego kodu do korzystania z zamka. Dotyczy to rzeczy takich jakCollections.synchronizedMap
(patrz javadoc).To zbyt uproszczone myślenie; samo pozbycie
synchronized(this)
się nie rozwiąże problemu. Właściwa synchronizacja przepustowości wymaga więcej przemyślenia.To jest wariant nr 1. Użycie
synchronized(this)
jest częścią twojego interfejsu. Jeśli nie chcesz tego ujawnić, nie rób tego.źródło
keySet()
ivalues()
nie blokują się (ich)this
, ale instancja mapy, co jest ważne, aby uzyskać spójne zachowanie dla wszystkich operacji na mapie. Przyczyną tego, że obiekt blokady jest rozłożony na zmienną, jest to, że podklasaSynchronizedSortedMap
potrzebuje go do implementacji podmap blokujących oryginalne wystąpienie mapy.Po pierwsze, należy zauważyć, że:
jest semantycznie równoważny z:
który jest jednym z powodów, aby nie używać
synchronized(this)
. Możesz argumentować, że możesz robić rzeczy wokółsynchronized(this)
bloku. Zwykle powodem jest unikanie w ogóle sprawdzania zsynchronizowanego, co prowadzi do różnego rodzaju problemów z współbieżnością, w szczególności problemu podwójnego sprawdzania blokowania , który pokazuje, jak trudne może być wykonanie stosunkowo prostej kontroli Threadsafe.Prywatny zamek to mechanizm obronny, który nigdy nie jest złym pomysłem.
Ponadto, jak wspomniałeś, prywatne zamki mogą kontrolować ziarnistość. Jeden zestaw operacji na obiekcie może być całkowicie niezwiązany z innym, ale
synchronized(this)
wzajemnie wyklucza dostęp do wszystkich z nich.synchronized(this)
po prostu nic ci nie daje.źródło
Podczas korzystania z synchronizacji (this) używasz instancji klasy jako samej blokady. Oznacza to, że gdy blokada jest uzyskiwana przez wątek 1 , wątek 2 powinien poczekać.
Załóżmy następujący kod:
Metoda 1 modyfikująca zmienną a i metoda 2 modyfikująca zmienną b , należy unikać jednoczesnej modyfikacji tej samej zmiennej dwoma wątkami i tak jest. ALE podczas wątku modyfikującego a i wątku modyfikującego b niej można wykonać bez żadnego wyścigu.
Niestety powyższy kod nie pozwala na to, ponieważ używamy tego samego odwołania do zamka; Oznacza to, że wątki, nawet jeśli nie są w stanie wyścigu, powinny czekać i oczywiście kod poświęca współbieżność programu.
Rozwiązaniem jest użycie 2 różnych blokad dla dwóch różnych zmiennych:
W powyższym przykładzie zastosowano bardziej drobnoziarniste zamki (2 zamki zamiast jednego ( lockA i lockB odpowiednio dla zmiennych a i b ), a w rezultacie pozwala na lepszą współbieżność, z drugiej strony stał się bardziej złożony niż pierwszy przykład ...
źródło
Chociaż zgadzam się, aby nie ślepo przestrzegać dogmatycznych zasad, to czy scenariusz „kradzieży zamków” wydaje ci się tak ekscentryczny? Wątek rzeczywiście może uzyskać blokadę obiektu „zewnętrznie” (
synchronized(theObject) {...}
), blokując inne wątki oczekujące na metody instancji synchronicznej.Jeśli nie wierzysz w złośliwy kod, zastanów się, że ten kod może pochodzić od stron trzecich (na przykład, jeśli opracujesz jakiś serwer aplikacji).
„Przypadkowa” wersja wydaje się mniej prawdopodobna, ale jak mówią, „zrób coś idiotycznego, a ktoś wymyśli lepszego idioty”.
Zgadzam się więc ze szkołą myślenia „zależy to od klasy”.
Edytuj następujące 3 pierwsze komentarze eljenso:
Nigdy nie spotkałem się z problemem kradzieży zamka, ale oto wyobrażony scenariusz:
Powiedzmy, że twój system to kontener serwletów, a przedmiotem, który rozważamy, jest
ServletContext
implementacja. TagetAttribute
metoda musi być bezpieczna dla wątków, ponieważ atrybuty kontekstu są danymi współużytkowanymi; więc deklarujesz to jakosynchronized
. Wyobraźmy sobie również, że udostępniasz publiczną usługę hostingową w oparciu o implementację kontenera.Jestem twoim klientem i wdrażam mój „dobry” serwlet na swojej stronie. Zdarza się, że mój kod zawiera wywołanie
getAttribute
.Haker, przebrany za innego klienta, wdraża swój złośliwy serwlet na twojej stronie. Zawiera następujący kod w pliku
init
metodzie:Zakładając, że dzielimy ten sam kontekst serwletu (dozwolony przez specyfikację, o ile dwa serwlety znajdują się na tym samym wirtualnym hoście), moje wywołanie
getAttribute
jest zablokowane na zawsze. Haker osiągnął DoS na moim serwletu.Ten atak nie jest możliwy, jeśli
getAttribute
jest zsynchronizowany z blokadą prywatną, ponieważ kod innej firmy nie może uzyskać tej blokady.Przyznaję, że ten przykład jest wymyślony i zbyt uproszczony pogląd na to, jak działa kontener serwletu, ale IMHO to potwierdza.
Wybrałbym więc projekt w oparciu o względy bezpieczeństwa: czy będę mieć pełną kontrolę nad kodem, który ma dostęp do instancji? Jakie byłyby konsekwencje trzymania przez wątek blokady na instancji w nieskończoność?
źródło
To zależy od sytuacji.
Jeśli istnieje tylko jedna jednostka udostępniania lub więcej niż jedna.
Zobacz pełny przykład działania tutaj
Małe wprowadzenie.
Wątki i elementy
możliwe do udostępnienia Dostęp do tego samego elementu może mieć wiele wątków, np. Wiele wątków połączenia udostępniających jedną wiadomość. Ponieważ wątki działają jednocześnie, może istnieć szansa na zastąpienie danych innymi, co może być błędem.
Potrzebujemy więc sposobu, aby zapewnić dostęp do elementu udostępnianego tylko przez jeden wątek na raz. (KONKURENCJA).
Blok
zsynchronizowany blok zsynchronizowany () jest sposobem na zapewnienie równoczesnego dostępu do jednostki współdzielonej.
Po pierwsze, mała analogia
Załóżmy, że w łazience są dwie osoby P1, P2 (wątki) Umywalka (wspólny obiekt) i są drzwi (zamek).
Teraz chcemy, aby jedna osoba korzystała z umywalki na raz.
Podejście polega na zablokowaniu drzwi przez P1, gdy drzwi są zamknięte P2 P2 czeka, aż p1 zakończy swoją pracę
P1 odblokuje drzwi,
wtedy tylko p1 może korzystać z umywalki.
składnia.
„to” zapewniło wewnętrzną blokadę związaną z klasą (programista Java zaprojektował klasę Object w taki sposób, że każdy obiekt może działać jako monitor). Powyższe podejście działa dobrze, gdy istnieje tylko jeden wspólny byt i wiele wątków (1: N). N współużytkowanych podmiotów - wątki M Pomyślmy teraz o sytuacji, gdy w umywalni znajdują się dwie umywalki i tylko jedne drzwi. Jeśli stosujemy poprzednie podejście, tylko p1 może korzystać z jednej umywalki na raz, podczas gdy p2 będzie czekać na zewnątrz. Jest to marnotrawstwo zasobów, ponieważ nikt nie używa B2 (umywalki). Mądrzejszym rozwiązaniem byłoby stworzenie mniejszego pokoju w łazience i zapewnienie im jednych drzwi na umywalkę. W ten sposób P1 może uzyskać dostęp do B1, a P2 może uzyskać dostęp do B2 i odwrotnie.
Zobacz więcej na Wątki ----> tutaj
źródło
W obozach C # i Java wydaje się inny konsensus. Większość kodu Java, który widziałem, wykorzystuje:
mając na uwadze, że większość kodu C # wybiera prawdopodobnie bezpieczniejsze:
Idiom C # jest z pewnością bezpieczniejszy. Jak wspomniano wcześniej, nie można uzyskać złośliwego / przypadkowego dostępu do zamka spoza instancji. Kod Java również ma to ryzyko, ale wydaje się, że społeczność Java z czasem przeszła do nieco mniej bezpiecznej, ale nieco bardziej zwięzłej wersji.
To nie ma oznaczać kopania w Javie, tylko odzwierciedlenie mojego doświadczenia w pracy w obu językach.
źródło
java.util.concurrent
Pakiet znacznie zmniejsza złożoność mojego bezpiecznego kodu gwintu. Mam tylko niepotwierdzone dowody na kontynuację, ale większość pracy, jaką widziałem,synchronized(x)
wydaje się polegać na ponownym wdrażaniu blokady, semafora lub zatrzasku, ale przy użyciu monitorów niższego poziomu.Mając to na uwadze, synchronizacja przy użyciu dowolnego z tych mechanizmów jest analogiczna do synchronizacji na obiekcie wewnętrznym, a nie przecieka blokady. Jest to korzystne, ponieważ masz absolutną pewność, że kontrolujesz wejście do monitora za pomocą dwóch lub więcej wątków.
źródło
final
zmienne)Lock
interfejs API]Przykładowy kod do użycia,
ReentrantLock
który implementujeLock
interfejsZalety Lock over Synchronized (to)
Zastosowanie zsynchronizowanych metod lub instrukcji wymusza, aby wszystkie pozyskiwanie i zwalnianie blokady odbywało się w sposób blokowy.
Implementacje blokady zapewniają dodatkową funkcjonalność w stosunku do korzystania ze zsynchronizowanych metod i instrukcji
tryLock()
)lockInterruptibly()
)tryLock(long, TimeUnit)
).Klasa Lock może również zapewniać zachowanie i semantykę, które są zupełnie inne niż niejawna blokada monitora, na przykład
Spójrz na to pytanie SE dotyczące różnych rodzajów
Locks
:Synchronizacja vs Blokada
Bezpieczeństwo wątków można osiągnąć, używając zaawansowanego interfejsu API współbieżności zamiast bloków synchronicznych. Ta strona dokumentacji zawiera dobre konstrukcje programistyczne do osiągnięcia bezpieczeństwa wątków.
Zablokuj obiekty obsługują idiomy blokujące, które upraszczają wiele jednoczesnych aplikacji.
Wykonawcy definiują interfejs API wysokiego poziomu do uruchamiania wątków i zarządzania nimi. Implementacje executorów dostarczone przez java.util.concurrent zapewniają zarządzanie pulą wątków odpowiednie dla aplikacji na dużą skalę.
Współbieżne kolekcje ułatwiają zarządzanie dużymi zbiorami danych i mogą znacznie zmniejszyć potrzebę synchronizacji.
Zmienne atomowe mają funkcje minimalizujące synchronizację i pomagające uniknąć błędów spójności pamięci.
ThreadLocalRandom (w JDK 7) zapewnia wydajne generowanie liczb pseudolosowych z wielu wątków.
Zapoznaj się także z pakietami java.util.concurrent i java.util.concurrent.atomic, aby zapoznać się z innymi konstrukcjami programistycznymi.
źródło
Jeśli zdecydowałeś, że:
wtedy nie widzę tabu zsynchronizowanego (this).
Niektóre osoby celowo używają synchronizacji (tej) (zamiast oznaczania metody synchronizowanej) w całej zawartości metody, ponieważ uważają, że „czytelnik jest bardziej zrozumiały”, na którym obiekcie jest faktycznie synchronizowany. Tak długo, jak ludzie dokonują świadomego wyboru (np. Rozumieją, że robiąc to, w rzeczywistości wstawiają dodatkowe kody bajtowe do metody, co może mieć wpływ na potencjalne optymalizacje), nie widzę w tym szczególnie problemu . Zawsze powinieneś dokumentować równoczesne zachowanie twojego programu, więc nie widzę argumentu „zsynchronizowane” publikuje zachowanie jako tak przekonującego.
Jeśli chodzi o pytanie, którą blokadę obiektu powinieneś użyć, myślę, że nie ma nic złego w synchronizacji na bieżącym obiekcie, jeśli byłoby to oczekiwane na podstawie logiki tego, co robisz i tego, w jaki sposób klasa byłaby zwykle używana . Na przykład w przypadku kolekcji obiekt, którego logicznie można się spodziewać, to zazwyczaj sama kolekcja.
źródło
Myślę, że istnieje dobre wytłumaczenie, dlaczego każda z tych technik jest ważna dla Ciebie w książce Brian Goetz zatytułowanej Java Concurrency In Practice. Wyjaśnia jeden punkt bardzo jasno - musisz użyć tego samego zamka „WSZĘDZIE”, aby chronić stan swojego obiektu. Metoda zsynchronizowana i synchronizacja na obiekcie często idą w parze. Np. Vector synchronizuje wszystkie swoje metody. Jeśli masz uchwyt do obiektu wektorowego i zamierzasz wykonać polecenie „wstaw, jeśli jest nieobecny”, to jedynie synchronizacja wektorowa własnych metod nie ochroni cię przed uszkodzeniem stanu. Musisz zsynchronizować za pomocą synchronizowanego (vectorHandle). Spowoduje to, że blokada SAME zostanie przejęta przez każdy wątek z uchwytem do wektora i ochroni ogólny stan wektora. Nazywa się to blokowaniem po stronie klienta. Wiemy, że wektor synchronizuje (to) / synchronizuje wszystkie swoje metody, a zatem synchronizacja na obiekcie vectorHandle spowoduje prawidłową synchronizację stanu obiektów wektorowych. Głupotą jest wierzyć, że jesteś bezpieczny dla wątków tylko dlatego, że używasz kolekcji dla wątków. Właśnie z tego powodu ConcurrentHashMap wyraźnie wprowadził metodę putIfAbsent - aby takie operacje były atomowe.
W podsumowaniu
źródło
Nie, nie zawsze powinieneś . Jednak staram się go unikać, gdy istnieje wiele obaw dotyczących konkretnego obiektu, które muszą być bezpieczne tylko dla siebie. Na przykład, możesz mieć zmienny obiekt danych, który ma pola „label” i „parent”; te muszą być bezpieczne dla wątków, ale zmiana jednego nie musi blokować drugiego zapisu i odczytu. (W praktyce unikałbym tego, deklarując zmienność pól i / lub używając opakowań AtomicFoo z java.util.concurrent).
Synchronizacja w ogóle jest nieco niezdarna, ponieważ uderza w dużą blokadę, zamiast myśleć dokładnie, jak wątki mogą ze sobą współpracować. Używanie
synchronized(this)
jest jeszcze bardziej nieporadne i antyspołeczne, ponieważ mówi „nikt nie może nic zmienić w tej klasie, dopóki trzymam zamek”. Jak często musisz to robić?Wolałbym mieć bardziej szczegółowe zamki; nawet jeśli chcesz zatrzymać wszystko przed zmianą (być może szeregujesz obiekt), możesz po prostu zdobyć wszystkie blokady, aby osiągnąć to samo, a dodatkowo jest to bardziej wyraźne. Kiedy używasz
synchronized(this)
, nie jest jasne, dlaczego synchronizujesz ani jakie mogą być skutki uboczne. Jeśli używaszsynchronized(labelMonitor)
, a nawet lepiejlabelLock.getWriteLock().lock()
, jasne jest, co robisz i do czego ograniczone są efekty twojej krytycznej sekcji.źródło
Krótka odpowiedź : musisz zrozumieć różnicę i dokonać wyboru w zależności od kodu.
Długa odpowiedź : ogólnie wolałbym unikać synchronizacji (tej), aby zmniejszyć rywalizację, ale prywatne zamki zwiększają złożoność, o której musisz wiedzieć. Więc użyj właściwej synchronizacji dla właściwego zadania. Jeśli nie masz doświadczenia w programowaniu wielowątkowym, wolę trzymać się blokowania instancji i poczytać na ten temat. (To powiedziawszy: samo użycie synchronizacji (this) nie powoduje, że twoja klasa jest w pełni bezpieczna dla wątków.) To nie jest łatwy temat, ale kiedy już się przyzwyczaisz, odpowiedź, czy użyć synchronizacji (this), czy nie, przychodzi naturalnie .
źródło
Blokada służy do widoczności lub do ochrony niektórych danych przed jednoczesnymi modyfikacjami które mogą prowadzić do wyścigu.
Gdy potrzebujesz po prostu wykonać operacje typu pierwotnego, aby były atomowe, dostępne są opcje takie jak
AtomicInteger
i .Załóżmy jednak, że masz dwie liczby całkowite, które są ze sobą powiązane, takie jak
x
iy
współrzędnych, które są ze sobą powiązane i powinny zostać zmienione w sposób atomowej. Następnie chroniłbyś ich za pomocą tego samego zamka.Blokada powinna chronić tylko stan, który jest ze sobą powiązany. Nie mniej i nie więcej. Jeśli użyjesz
synchronized(this)
w każdej metodzie, to nawet jeśli stan klasy nie jest ze sobą powiązany, wszystkie wątki staną w obliczu niezgody, nawet jeśli zaktualizujesz stan niepowiązany.W powyższym przykładzie mam tylko jedną metodę, która mutuje obie,
x
ay
nie dwie różne metody jakox
iy
są powiązane, a gdybym podał dwie różne metody mutacjix
iy
osobno, nie byłoby to bezpieczne dla wątków.Ten przykład ma na celu jedynie zademonstrowanie, a niekoniecznie sposób, w jaki należy go wdrożyć. Najlepszym sposobem, aby to zrobić, byłoby uczynienie go NIEZWYKŁA .
Teraz w przeciwieństwie do
Point
przykładu, istnieje przykładTwoCounters
podany już przez @Andreas, w którym stan chroniony przez dwie różne blokady, ponieważ stan nie jest ze sobą powiązany.Proces korzystania z różnych blokad w celu ochrony stanów niepowiązanych nazywa się Blokowaniem pasków lub Blokowaniem podziału
źródło
Powodem nie do synchronizowania na to , że czasem trzeba więcej niż jednego zamka (drugiego zamka często zostaje usunięty po pewnym dodatkowym myślenia, ale nadal trzeba go w stanie pośrednim). Jeśli zablokujesz na to , że zawsze trzeba pamiętać, której jeden z dwóch zamków jest to ; jeśli zablokujesz prywatny obiekt, nazwa zmiennej ci o tym powie.
Z punktu widzenia czytelnika, jeśli widzisz blokowanie tego , zawsze musisz odpowiedzieć na dwa pytania:
Przykład:
Jeśli dwa wątki zaczynają się
longOperation()
w dwóch różnych przypadkachBadObject
, nabywają blokady; kiedy nadszedł czas na wywołaniel.onMyEvent(...)
, mamy impas, ponieważ żaden z wątków nie może uzyskać blokady drugiego obiektu.W tym przykładzie możemy wyeliminować zakleszczenie, używając dwóch zamków, jednego dla krótkich operacji i jednego dla długich.
źródło
BadObject
wywołanie AlongOperation
przez B, przekazanie AmyListener
i odwrotnie. Nie niemożliwe, ale dość skomplikowane, potwierdzając moje wcześniejsze uwagi.Jak już powiedziano tutaj, zsynchronizowany blok może wykorzystywać zmienną zdefiniowaną przez użytkownika jako obiekt blokady, gdy funkcja synchronizacji używa tylko „tego”. I oczywiście możesz manipulować obszarami swojej funkcji, które powinny być zsynchronizowane i tak dalej.
Ale wszyscy mówią, że nie ma różnicy między funkcją zsynchronizowaną a blokiem, który obejmuje całą funkcję, używając „tego” jako obiektu blokady. To nieprawda, różnica polega na kodzie bajtów, który zostanie wygenerowany w obu sytuacjach. W przypadku użycia bloku zsynchronizowanego należy przypisać zmienną lokalną, która zawiera odniesienie do „tego”. W rezultacie będziemy mieli nieco większy rozmiar funkcji (nie dotyczy, jeśli masz tylko kilka funkcji).
Bardziej szczegółowe wyjaśnienie różnicy można znaleźć tutaj: http://www.artima.com/insidejvm/ed2/threadsynchP.html
Również użycie zsynchronizowanego bloku nie jest dobre ze względu na następujący punkt widzenia:
Aby uzyskać więcej informacji w tym obszarze, polecam przeczytanie tego artykułu: http://java.dzone.com/articles/synchronized-consisted
źródło
Jest to tak naprawdę tylko uzupełnienie innych odpowiedzi, ale jeśli twoim głównym sprzeciwem wobec używania prywatnych obiektów do blokowania jest to, że zaśmieca twoją klasę polami, które nie są związane z logiką biznesową, wtedy Project Lombok musi
@Synchronized
wygenerować płytkę zbiorczą w czasie kompilacji:kompiluje się do
źródło
Dobry przykład zastosowania zsynchronizowanego (this).
Jak widać tutaj, używamy do tego synchronizacji, aby łatwo współpracować metodą długiej (być może nieskończonej pętli uruchamiania) z niektórymi tam metodami synchronizowanymi.
Oczywiście można go bardzo łatwo przepisać przy użyciu synchronizacji na prywatnym polu. Ale czasami, kiedy mamy już pewne projektowanie z metodami zsynchronizowanymi (tj. Starszą klasę, z której wywodzimy, zsynchronizowane (to) może być jedynym rozwiązaniem).
źródło
this
. To może być pole prywatne.To zależy od zadania, które chcesz wykonać, ale nie użyłbym tego. Sprawdź także, czy nie można wykonać zapisywania wątku, którego chcesz osiągnąć, synchronizując (to)? W interfejsie API jest też kilka fajnych blokad, które mogą ci pomóc :)
źródło
Chcę tylko wspomnieć o możliwym rozwiązaniu dla unikatowych prywatnych odniesień w atomowych częściach kodu bez zależności. Możesz użyć statycznej Hashmapy z blokadami i prostej metody statycznej o nazwie atomic (), która automatycznie tworzy wymagane odwołania przy użyciu informacji o stosie (pełna nazwa klasy i numer linii). Następnie możesz użyć tej metody do synchronizacji instrukcji bez pisania nowego obiektu blokady.
źródło
Unikaj używania
synchronized(this)
jako mechanizmu blokującego: blokuje to instancję całej klasy i może powodować zakleszczenia. W takich przypadkach refaktoryzuj kod, aby zablokować tylko określoną metodę lub zmienną, w ten sposób cała klasa nie zostanie zablokowana.Synchronised
może być używany wewnątrz poziomu metody.Zamiast używać
synchronized(this)
poniższy kod pokazuje, jak możesz po prostu zablokować metodę.źródło
Moje dwa centy w 2019 r., Chociaż to pytanie można było już rozwiązać.
Blokowanie „tego” nie jest złe, jeśli wiesz, co robisz, ale poza sceną blokowanie „tego” jest (co niestety pozwala na to zsynchronizowane słowo kluczowe w definicji metody).
Jeśli naprawdę chcesz, aby użytkownicy twojej klasy mogli „ukraść” twoją blokadę (tj. Uniemożliwić radzenie sobie z nią przez inne wątki), tak naprawdę chcesz, aby wszystkie zsynchronizowane metody czekały na uruchomienie innej metody synchronizacji i tak dalej. Powinien być celowy i przemyślany (a zatem udokumentowany, aby pomóc użytkownikom w zrozumieniu tego).
W celu dalszego rozwinięcia, na odwrót, musisz wiedzieć, co „zyskujesz” (lub „tracisz” na), jeśli zablokujesz niedostępny zamek (nikt nie może „ukraść” zamka, masz całkowitą kontrolę i tak dalej. ..).
Problemem jest dla mnie to, że zsynchronizowane słowo kluczowe w podpisie definicji metody sprawia, że programiści po prostu zbyt łatwo nie zastanawiają się, co zablokować, co jest bardzo ważną rzeczą do przemyślenia, jeśli nie chcesz napotkać problemów w wielu -działany program.
Nie można argumentować, że „zwykle” nie chcesz, aby użytkownicy z twojej klasy mogli robić te rzeczy lub że „zwykle” chcesz ... Zależy to od tego, jaką funkcjonalność kodujesz. Nie można wprowadzić reguły kciuka, ponieważ nie można przewidzieć wszystkich przypadków użycia.
Rozważ np. Drukarkę, która korzysta z wewnętrznej blokady, ale wtedy ludzie mają trudności z użyciem jej z wielu wątków, jeśli nie chcą, aby ich wyniki się przeplatały.
Jeśli twoja blokada jest dostępna poza klasą, czy nie, to twoja decyzja jako programisty zależy od tego, jakie funkcje ma klasa. Jest to część interfejsu API. Nie można na przykład odejść od zsynchronizowanego (this) do zsynchronizowanego (provateObjet) bez ryzyka zerwania zmian w kodzie przy jego użyciu.
Uwaga 1: Wiem, że możesz osiągnąć wszystko, co zsynchronizowane (to) „osiąga”, używając jawnego obiektu blokady i ujawniając go, ale myślę, że nie jest konieczne, jeśli twoje zachowanie jest dobrze udokumentowane i naprawdę wiesz, co oznacza blokowanie na „to”.
Uwaga 2: Nie zgadzam się z argumentem, że jeśli jakiś kod przypadkowo kradnie blokadę, jest to błąd i musisz go rozwiązać. Jest to w pewnym sensie ten sam argument, co stwierdzenie, że mogę upublicznić wszystkie moje metody, nawet jeśli nie mają one być publiczne. Jeśli ktoś „przypadkowo” dzwoni do mojej prywatnej metody, to jest to błąd. Po co w ogóle włączać ten wypadek !!! Jeśli umiejętność kradzieży zamka jest problemem dla twojej klasy, nie pozwalaj na to. Tak proste jak to.
źródło
Myślę, że punkty jeden (ktoś inny używa twojej blokady) i dwa (wszystkie metody niepotrzebnie wykorzystujące tę samą blokadę) mogą się zdarzyć w każdej dość dużej aplikacji. Zwłaszcza, gdy nie ma dobrej komunikacji między programistami.
Nie jest odlewany w kamieniu, to głównie kwestia dobrych praktyk i zapobiegania błędom.
źródło