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 sécurisée pour les threads?
Puisque la méthode increment
est static
, elle se synchronisera sur l'objet classe pour le ThreadSafeClass
. La méthode 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 objets et donc deux threads différents pourront exécuter les méthodes en même temps. Depuis le ++
et --
les opérations ne sont pas atomiques, la classe n'est pas thread-safe.
De plus, puisque count
est static
, la modifier à partir de decrement
qui est une méthode instance synchronisée n'est pas sûre car elle peut être appelée sur différentes instances et modifier count
simultanément de cette façon.
Vous avez deux méthodes synchronisées, mais l'une d'entre elles est statique et l'autre ne l'est pas. Lors de l'accès à une méthode synchronisée, en fonction de son type (statique ou non statique), un objet différent sera verrouillé. Pour une méthode statique, un verrou sera placé sur l'objet Class, tandis que pour le bloc non statique, un verrou sera placé sur l'instance de la classe qui exécute la méthode. Parce que vous avez deux objets verrouillés différents, vous pouvez avoir deux threads qui modifient le même objet simultanément.
Quelqu'un peut-il expliquer pourquoi la classe ci-dessus n'est pas thread-safe?
increment
étant statique, la synchronisation se fera sur la classe elle-même.decrement
n'étant pas statique, la synchronisation se fera sur l'instanciation de l'objet, mais cela ne sécurise rien car count
est statique.Je voudrais ajouter que pour déclarer un compteur thread-safe, je pense que la façon la plus simple est d'utiliser AtomicInteger
au lieu d'un int primitif.
Permettez-moi de vous rediriger vers le Java.util.concurrent.atomic
package-info.
Les réponses des autres sont assez bonnes pour expliquer la raison. Je viens d'ajouter quelque chose pour 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'objet d'instance. synchronized
on fun4
est synchronisé au niveau de l'objet classe. Ce qui signifie:
a1.fun1()
en même temps, ce dernier appel sera bloqué.a1.fun1()
et le thread 2 appelle a1.fun2()
en même temps, ce dernier appel est bloqué.a1.fun1()
et le thread 2 appelle a1.fun3()
en même temps, aucun 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 appels seront bloqués car synchronized
sur fun4
Est au niveau de la classe.A.fun4()
, le thread 2 appelle a1.fun1()
en même temps, aucun blocage, les 2 méthodes seront exécutées en même temps.decrement
se verrouille sur une chose différente de increment
afin qu'ils ne s'empêchent pas de s'exécuter.decrement
sur une instance revient à verrouiller une autre chose que d'appeler decrement
sur une autre instance, mais ils affectent la même chose.Le premier signifie que les appels qui se chevauchent vers increment
et decrement
peuvent entraîner une annulation (correcte), un incrément ou une décrémentation.
Le second signifie que deux appels qui se chevauchent vers decrement
sur des instances différentes peuvent entraîner un décrément double (correct) ou un décrément unique.
Comme expliqué dans d'autres réponses, votre code est pas Thread safe puisque la méthode statique increment()
verrouille Moniteur de classe et méthode non statique decrement()
verrouille le moniteur d'objet.
Pour cet exemple de code, une meilleure solution existe sans utilisation du mot clé synchronzed
. Vous devez utiliser AtomicInteger pour assurer 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();
}
}