Pourquoi cette classe n’est-elle pas thread-safe?

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 .

  1. decrement se verrouille sur une autre chose à increment pour qu’ils ne s’empêchent pas de fonctionner.
  2. Appeler le 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:

  1. Lorsque 2 threads appellent a1.fun1() en même temps, le dernier appel sera bloqué.
  2. Lorsque le thread 1 appelle a1.fun1() et le thread 2 appelle a1.fun2() en même temps, le dernier appel sera bloqué.
  3. Lorsque le thread 1 appelle 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.
  4. Lorsque le thread 1 appelle 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.
  5. Lorsque le thread 1 appelle 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(); } }