Co należy synchronizować w sekcjach krytycznych Javy?

W Javie idiomatyczny sposób deklarowania sekcji krytycznych w kodzie jest następujący:

private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}

Prawie wszystkie bloki synchronizują się na this, ale czy jest ku temu jakiś szczególny powód? Czy są inne możliwości? Czy istnieją jakieś najlepsze praktyki dotyczące tego, na którym obiekcie należy synchronizować? (np. prywatne instancje Object?)

Author: shsteimer, 2009-01-06

9 answers

Po pierwsze, zauważ, że poniższe fragmenty kodu są identyczne.

public void foo() {
    synchronized (this) {
        // do something thread-safe
    }
}

I:

public synchronized void foo() {
    // do something thread-safe
}

Zrób dokładnie to samo . Brak preferencji dla żadnego z nich, z wyjątkiem czytelności kodu i stylu.

Kiedy synchronizujesz metody lub bloki kodu, ważne jest, aby wiedzieć dlaczego robisz coś takiego i jaki obiekt dokładnie blokujesz i dla w jakim celu.

Zauważ również, że są sytuacje, w których będzie chciał zsynchronizować bloki kodu, w których monitor, o który prosisz (tj. obiekt zsynchronizowany), niekoniecznie jest this, jak w tym przykładzie:
Vector v = getSomeGlobalVector();
synchronized (v) {
    // some thread-safe operation on the vector
}

Sugeruję zdobycie większej wiedzy na temat programowania współbieżnego, będzie Ci to bardzo przydatne, gdy dokładnie dowiesz się, co dzieje się za kulisami. Powinieneś sprawdzić Programowanie współbieżne w Javie , świetną książkę na ten temat. Jeśli chcesz szybko zagłębić się w temat, sprawdź out Java @ Sun

 45
Author: Yuval Adam,
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
2009-01-07 11:21:38

Jak zauważyli wcześniej answers, najlepszą praktyką jest synchronizacja na obiekcie o ograniczonym zakresie (innymi słowy, wybierz najbardziej restrykcyjny zakres, z którym możesz uciec, i użyj go.) W szczególności synchronizacja na this jest złym pomysłem, chyba że zamierzasz pozwolić użytkownikom twojej klasy na zdobycie blokady.

Szczególnie brzydki przypadek pojawia się jednak, jeśli zdecydujesz się na synchronizację na java.lang.String. Ciągi mogą być (i w praktyce prawie zawsze są) internowane. Oznacza to, że każdy ciąg o jednakowej zawartości-w całym JVM - okazuje się być tym samym ciągiem za kulisami. Oznacza to, że jeśli zsynchronizujesz dowolny ciąg znaków, Inna (całkowicie odmienna) sekcja kodu, która również blokuje ciąg o tej samej zawartości, faktycznie zablokuje również Twój kod.

Kiedyś rozwiązywałem problem z impasu w systemie produkcyjnym i (bardzo boleśnie) śledziłem impas do dwóch zupełnie różnych pakietów open source, które każdy zsynchronizował na instancji String których zawartość była obie "LOCK".

 58
Author: Jared,
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
2009-01-13 16:12:21

Staram się unikać synchronizacji na this, ponieważ pozwoliłoby to wszystkim z zewnątrz, którzy mieli odniesienie do tego obiektu, zablokować moją synchronizację. Zamiast tego tworzę lokalny obiekt synchronizacji:

public class Foo {
    private final Object syncObject = new Object();
    …
}

Teraz mogę użyć tego obiektu do synchronizacji bez obawy, że ktoś "ukradnie" zamek.

 37
Author: Bombe,
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-01-26 14:57:12

Tylko dla podkreślenia, że istnieją również ReadWriteLocks dostępne w Javie, znalezione jako java.util./ align = "left" / zamki.ReadWriteLock.

W większości moich zastosowań oddzielam blokadę jako "do czytania" i "do aktualizacji". Jeśli po prostu użyjesz zsynchronizowanego słowa kluczowego, wszystkie odczyty tej samej metody / bloku kodu będą "w kolejce". Tylko jeden wątek może uzyskać dostęp do bloku w tym samym czasie.

W większości przypadków nie musisz się martwić o problemy z współbieżnością, jeśli po prostu czytasz. Tak jest, gdy jesteś pisząc, że martwisz się o równoczesne aktualizacje (w wyniku utraty danych) lub czytanie podczas zapisu (częściowe aktualizacje), że musisz się martwić.

Dlatego blokada odczytu/zapisu ma dla mnie większy sens podczas programowania wielowątkowego.

 6
Author: Kent Lai,
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
2009-01-06 14:41:41

Będziesz chciał zsynchronizować obiekt, który może służyć jako Mutex. Jeśli bieżąca instancja (odniesienie to) jest odpowiednia (Nie Singleton, na przykład), możesz jej użyć, ponieważ w Javie każdy obiekt może służyć jako Mutex.

W innych przypadkach, możesz chcieć współdzielić Mutex między kilkoma klasami, jeśli instancje tych klas mogą potrzebować dostępu do tych samych zasobów.

To zależy w dużym stopniu od środowiska, w którym pracujesz i rodzaju systemu, w którym jesteś budynek. W większości aplikacji Java EE, które widziałem, nie ma rzeczywistej potrzeby synchronizacji...

 4
Author: Electric Monk,
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
2009-01-06 11:37:20

Osobiście uważam, że odpowiedzi, które twierdzą, że synchronizacja nigdy nie jest lub tylko rzadko jest poprawna, są błędne. Myślę, że to zależy od Twojego API. Jeśli twoja klasa jest implementacją threadsafe i tak ją udokumentujesz, powinieneś użyć this. Jeśli synchronizacja nie ma sprawić, że każda instancja klasy jako cały wątek będzie bezpieczna w wywołaniu jej publicznych metod, powinieneś użyć prywatnego obiektu wewnętrznego. Komponenty Biblioteki wielokrotnego użytku często wchodzą w pierwsze category - musisz dokładnie przemyśleć, zanim nie pozwolisz użytkownikowi owinąć API w zewnętrzną synchronizację.

W pierwszym przypadku użycie this pozwala na wywołanie wielu metod w sposób atomowy. Jednym z przykładów jest PrintWriter, gdzie możesz chcieć wypisać wiele linii (powiedzmy ślad stosu do konsoli / REJESTRATORA) i zagwarantować, że pojawią się razem - w tym przypadku fakt, że ukrywa obiekt synchronizacji wewnętrznie, jest prawdziwym bólem. Innym takim przykładem jest zbiór zsynchronizowany wrappers - tam musisz zsynchronizować obiekt collection, aby iterację wykonać; ponieważ iteracja składa się z wielu wywołań metod, nie możesz chronić go całkowicie wewnętrznie.

W tym drugim przypadku używam zwykłego obiektu:

private Object mutex=new Object();

Jednak po zaobserwowaniu wielu zrzutów JVM i śladów stosu, które mówią, że blokada jest " instancją Javy.lang.Object () " muszę powiedzieć, że użycie klasy wewnętrznej może być często bardziej pomocne, jak sugerowali inni.

W każdym razie, to jest warte moje dwa bity.

Edit: jeszcze jedno, podczas synchronizacji na this wolę zsynchronizować metody i zachować metody bardzo szczegółowe. Myślę, że jest jaśniejszy i bardziej zwięzły.

 4
Author: Lawrence Dol,
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-03-29 18:51:09

Synchronizacja w Javie często polega na synchronizacji operacji na tej samej instancji. Synchronizacja na this jest bardzo idiomatyczna, ponieważ this jest współdzielonym odniesieniem, które jest automatycznie dostępne między różnymi metodami instancji (lub sekcjami) w klasie.

Używanie innego odniesienia specjalnie do blokowania, na przykład przez deklarowanie i inicjowanie prywatnego pola Object lock = new Object(), jest czymś, czego nigdy nie potrzebowałem ani nie używałem. Myślę, że jest to przydatne tylko wtedy, gdy potrzebujesz zewnętrznej synchronizacji na dwóch lub więcej niezsynchronizowanych zasobach wewnątrz obiektu, chociaż zawsze starałbym się refaktorować taką sytuację do prostszej formy.

W każdym razie, implicit (metoda synchronizowana) lub explicit synchronized(this) jest często używany, również w bibliotekach Javy. Jest to dobry idiom i, jeśli dotyczy, zawsze powinien być twoim pierwszym wyborem.

 2
Author: eljenso,
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
2009-01-06 12:26:53

To, co synchronizujesz, zależy od tego, jakie inne wątki, które mogą potencjalnie wejść w konflikt z wywołaniem tej metody, mogą synchronizować.

Jeśli {[0] } jest obiektem używanym tylko przez jeden wątek i uzyskujemy dostęp do mutowalnego obiektu, który jest współdzielony między wątkami, dobrym kandydatem jest synchronizacja nad tym obiektem - synchronizacja na this nie ma sensu, ponieważ inny wątek, który modyfikuje ten współdzielony obiekt, może nawet nie wiedzieć this, ale zna ten obiekt.

Na Inna ręczna synchronizacja przez this ma sens, jeśli wiele wątków wywołuje metody tego obiektu w tym samym czasie, na przykład jeśli jesteśmy w singletonie.

Zauważ, że metoda synchronizowana często nie jest najlepszą opcją, ponieważ trzymamy blokadę przez cały czas działania metody. Jeśli zawiera timeconsuming ale thread bezpieczne części, i nie tak czasochłonne wątku niebezpieczne części, synchronizacja nad metodą jest bardzo źle.

 1
Author: Hans-Peter Störr,
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
2010-06-23 13:43:55

Prawie wszystkie bloki synchronizują się na tym, ale czy jest jakiś szczególny powód? Czy są inne możliwości?

Ta deklaracja synchronizuje całą metodę.

private synchronized void doSomething() {

Ta deklaracja zsynchronizowała część bloku kodu zamiast całej metody.

private void doSomething() {
  // thread-safe code
  synchronized(this) {
    // thread-unsafe code
  }
  // thread-safe code
}

Z dokumentacji oracle Strona

Zsynchronizowanie tych metod ma dwa efekty:

Po pierwsze, nie jest możliwe dwa wywołania zsynchronizowanych metod na ten sam obiekt do przeplatania . Gdy jeden wątek wykonuje zsynchronizowaną metodę dla obiektu, wszystkie inne wątki, które wywołują zsynchronizowane metody dla tego samego obiektu blokują (wstrzymują wykonywanie), dopóki pierwszy wątek nie zostanie wykonany z obiektem.

Czy są inne możliwości? Czy istnieją jakieś najlepsze praktyki dotyczące tego, na którym obiekcie należy synchronizować? (np. prywatne instancje obiektu?)

Istnieje wiele możliwości i alternatyw dla synchronizacji. Możesz zrobić Twój wątek kodu jest bezpieczny dzięki użyciu interfejsów API wysokiego poziomu (dostępnych od wersji JDK 1.5)
Lock objects
Executors
Concurrent collections
Atomic variables
ThreadLocalRandom

Zobacz poniższe pytania SE, aby uzyskać więcej szczegółów:

Synchronizacja vs Lock

Unikaj synchronizacji (tego) w Javie?

 0
Author: Ravindra babu,
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:02:51