class ThreadSafeClass extends Thread
{
private static int count = 0;
public synchronized static void increment()
{
count++;
}
public synchronized void decrement()
{
count--;
}
}
Czy ktoś może wyjaśnić, dlaczego powyższa klasa nie jest bezpieczna wątkowo?
java
multithreading
thread-safety
das kinder
źródło
źródło
increment
), będzie to bezpieczne wątkowo. Lub jeśli użyłeś jakiegoś obiektu blokującego. Jak powiedziałem, nie znam Javy - mój komentarz wynika ze znajomości języka C #.synchronized
należy jej używać tylko w metodach statycznych. Więc moim zdaniem, nawet jeśli usunieszincrement
metodę, nadal nie jest ona bezpieczna wątkowo, ponieważ dwie instancje (które mają tylko zsynchronizowany dostęp przez tę samą instancję) mogą wywoływać metodę jednocześnie.Odpowiedzi:
Ponieważ
increment
metoda jest takastatic
, będzie synchronizować obiekt klasy dlaThreadSafeClass
.decrement
Metoda nie jest statyczny i zsynchronizuje na wystąpienie używane to nazwać. Oznacza to, że będą synchronizować się na różnych obiektach, a zatem dwa różne wątki mogą wykonywać te metody w tym samym czasie. Ponieważ operacje++
i--
nie są atomowe, klasa nie jest bezpieczna wątkowo.Ponadto, ponieważ
count
jeststatic
, modyfikowanie jej, zdecrement
której jest to metoda zsynchronizowanej instancji , jest niebezpieczne, ponieważ można ją wywołać w różnych instancjach icount
jednocześnie modyfikować w ten sposób.źródło
count
jeststatic
, posiadanie metody wystąpieniadecrement()
jest niewłaściwe, nawet jeśli nie byłostatic
increment()
metody, ponieważ dwa wątki mogą wywoływaćdecrement()
w różnych instancjach, modyfikując ten sam licznik jednocześnie.synchronized
bloków w ogóle (nawet na całej zawartości metoda) zamiast przy użyciu modyfikatora na metodzie, czylisynchronized(this) { ... }
asynchronized(ThreadSafeClass.class) { ... }
.++
i--
nie są atomowe, nawet włączonevolatile int
.Synchronized
rozwiązuje problem odczytu / aktualizacji / zapisu z++
/--
, alestatic
słowem kluczowym jest tutaj klucz . Dobra odpowiedź!synchronized
metody instancji. Po prostu nie oczekuj, że metoda „zsynchronizowana” w metodzie instancji zapewni ochronę danych statycznych. Jedynym problemem jest to, co powiedziałeś w pierwszym akapicie: metody używają różnych blokad w celu ochrony tych samych danych, a to oczywiście nie zapewnia żadnej ochrony.Masz dwie zsynchronizowane metody, ale jedna z nich jest statyczna, a druga nie. Podczas uzyskiwania dostępu do metody zsynchronizowanej, na podstawie jej typu (statyczna lub niestatyczna), inny obiekt zostanie zablokowany. W przypadku metody statycznej blokada zostanie nałożona na obiekt Class, natomiast w przypadku bloku niestatycznego blokada zostanie nałożona na wystąpienie klasy, która uruchamia metodę. Ponieważ masz dwa różne zablokowane obiekty, możesz mieć dwa wątki, które jednocześnie modyfikują ten sam obiekt.
źródło
increment
ponieważ jest statyczny, synchronizacja zostanie wykonana na samej klasie.decrement
ponieważ nie jest statyczny, synchronizacja zostanie wykonana na instancji obiektu, ale to nie zapewnia niczego, ponieważcount
jest statyczne.Chciałbym dodać, że aby zadeklarować licznik bezpieczny dla wątków, moim zdaniem najprostszym sposobem jest użycie
AtomicInteger
zamiast pierwotnego int.Przekieruję cię do informacji o
java.util.concurrent.atomic
paczce.źródło
Odpowiedzi innych są całkiem dobre, wyjaśniają powód. Dodam tylko coś do podsumowania
synchronized
:public class A { public synchronized void fun1() {} public synchronized void fun2() {} public void fun3() {} public static synchronized void fun4() {} public static void fun5() {} } A a1 = new A();
synchronized
włączonyfun1
ifun2
jest synchronizowany na poziomie obiektu instancji.synchronized
onfun4
jest synchronizowany na poziomie obiektu klasy. Co znaczy:a1.fun1()
w tym samym czasie, drugie połączenie zostanie zablokowane.a1.fun1()
i wywołanie wątku 2a1.fun2()
w tym samym czasie, drugie wywołanie zostanie zablokowane.a1.fun1()
i wywołanie wątku 2a1.fun3()
w tym samym czasie, bez blokowania, dwie metody zostaną wykonane w tym samym czasie.A.fun4()
, w przypadku wywołania innych wątkówA.fun4()
lubA.fun5()
w tym samym czasie, ostatnie wywołania będą blokowane, ponieważsynchronized
onfun4
jest na poziomie klasy.A.fun4()
wątku 2a1.fun1()
w tym samym czasie, bez blokowania, 2 metody zostaną wykonane w tym samym czasie.źródło
decrement
blokuje się na innej rzeczy, abyincrement
nie przeszkadzać sobie nawzajem.decrement
jednej instancji blokuje inną rzecz niż wywołaniedecrement
innej instancji, ale wpływa to na to samo.Pierwsza oznacza, że nakładające się wywołania
increment
idecrement
mogą skutkować anulowaniem (poprawnym), zwiększeniem lub zmniejszeniem.Drugi oznacza, że dwa nakładające się wywołania
decrement
w różnych instancjach mogą spowodować podwójne zmniejszenie (poprawne) lub pojedyncze zmniejszenie.źródło
Ponieważ dwie różne metody, jedna to poziom instancji, a druga to poziom klasy, musisz zablokować 2 różne obiekty, aby było to ThreadSafe
źródło
Jak wyjaśniono w innych odpowiedziach, Twój kod nie jest bezpieczny dla wątków, ponieważ metoda statyczna
increment()
blokuje Monitor klas i metoda niestatycznadecrement()
blokuje Monitor obiektów.W przypadku tego przykładu kodu istnieje lepsze rozwiązanie bez
synchronzed
użycia słów kluczowych. Aby zapewnić bezpieczeństwo wątków, musisz użyć AtomicInteger .Bezpieczny wątek przy użyciu
AtomicInteger
:import java.util.concurrent.atomic.AtomicInteger; class ThreadSafeClass extends Thread { private static AtomicInteger count = new AtomicInteger(0); public static void increment() { count.incrementAndGet(); } public static void decrement() { count.decrementAndGet(); } public static int value() { return count.get(); } }
źródło