class ThreadSafeClass extends Thread { private static int count = 0; public synchronized static void increment() { count++; } public synchronized void decrement() { count--; } }
Quelqu’un peut-il expliquer pourquoi la classe ci-dessus n’est pas thread-safe?
Comme la méthode d’ increment
est static
elle sera synchronisée sur l’object de classe pour ThreadSafeClass
. La méthode de decrement
n’est pas statique et se synchronisera sur l’instance utilisée pour l’appeler. C’est-à-dire qu’ils se synchroniseront sur différents objects et donc deux threads différents peuvent exécuter les méthodes en même temps. Puisque les opérations ++
et --
ne sont pas atomiques, la classe n’est pas thread-safe.
De plus, comme count
est static
, sa modification à partir du decrement
qui est une méthode d’ instance synchronisée, n’est pas sûre car elle peut être appelée sur différentes instances et modifier le count
simultanément.
Vous avez deux méthodes synchronisées, mais l’une d’entre elles est statique et l’autre non. Lors de l’access à une méthode synchronisée, basée sur son type (statique ou non statique), un object différent sera verrouillé. Pour une méthode statique, un verrou sera placé sur l’object Class, tandis que pour le bloc non statique, un verrou sera placé sur l’instance de la classe qui exécute la méthode. Étant donné que vous avez deux objects verrouillés différents, vous pouvez avoir deux threads qui modifient le même object simultanément.
Quelqu’un peut-il expliquer pourquoi la classe ci-dessus n’est pas thread-safe?
increment
étant statique, la synchronisation sera faite sur la classe elle-même. decrement
étant non statique, la synchronisation sera faite sur l’instanciation de l’object, mais cela ne sécurise rien car count
est statique. J’aimerais append que pour déclarer un compteur thread-safe, le moyen le plus simple est d’utiliser AtomicInteger
au lieu d’un int primitif.
Permettez-moi de vous redirect vers le package-info java.util.concurrent.atomic
.
decrement
se verrouille sur une autre chose à increment
pour qu’ils ne s’empêchent pas de fonctionner. decrement
sur une instance verrouille une autre façon d’appeler le decrement
sur une autre instance, mais cela affecte la même chose. Le premier signifie que le chevauchement des appels à l’ increment
et à la decrement
peut entraîner une annulation (correcte), un incrément ou une décrémentation.
La seconde signifie que deux appels se chevauchant pour decrement
des instances différentes peuvent entraîner une double décrémentation (correcte) ou une décrémentation unique.
Les réponses des autres sont assez bonnes pour expliquer la raison. Je viens d’append quelque chose à résumer 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
sur fun1
et fun2
est synchronisé au niveau de l’object instance. synchronized
sur fun4
est synchronisé au niveau de l’object de classe. Ce qui signifie:
a1.fun1()
en même temps, le dernier appel sera bloqué. a1.fun1()
et le thread 2 appelle a1.fun2()
en même temps, le dernier appel sera bloqué. a1.fun1()
et le thread 2 appelle a1.fun3()
en même temps, pas de blocage, les 2 méthodes seront exécutées en même temps. A.fun4()
, si d’autres threads appellent A.fun4()
ou A.fun5()
en même temps, ces derniers seront bloqués car synchronized
sur fun4
sont au niveau de la classe. A.fun4()
, le thread 2 appelle a1.fun1()
en même temps, pas de blocage, les 2 méthodes seront exécutées en même temps. Étant donné que deux méthodes différentes sont utilisées, l’une au niveau de l’instance et l’autre au niveau de la classe, vous devez verrouiller 2 objects différents pour la rendre ThreadSafe.
Comme expliqué dans d’autres réponses, votre code n’est pas sûr pour les threads, car la méthode statique increment()
verrouille le moniteur de classes et les méthodes non statiques decrement()
verrous Moniteur d’objects.
Pour cet exemple de code, une meilleure solution existe sans l’utilisation de mots clés synchronzed
. Vous devez utiliser AtomicInteger pour atteindre la sécurité des threads.
Thread safe en utilisant 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(); } }