Quel est le problème de concurrence d'accès le plus fréquent que vous ayez rencontré en Java? [fermé]

192

Il s'agit d'une sorte de sondage sur les problèmes de concurrence courants en Java. Un exemple pourrait être le blocage classique ou la condition de concurrence ou peut-être des bogues de thread EDT dans Swing. Je m'intéresse à la fois à un large éventail de problèmes possibles, mais également aux problèmes les plus courants. Donc, veuillez laisser une réponse spécifique à un bogue de concurrence Java par commentaire et votez si vous en voyez une.

Alex Miller
la source
16
Pourquoi est-ce fermé? Ceci est utile à la fois pour les autres programmeurs qui demandent la concurrence en Java et pour avoir une idée des classes de défauts de concurrence qui sont les plus observées par les autres développeurs Java.
L̲̳o̲̳̳n̲̳̳g̲̳̳p̲̳o̲̳̳k̲̳̳e̲̳̳
@Longpoke Le message de fermeture explique pourquoi il est fermé. Ce n'est pas une question avec une réponse "correcte" spécifique, c'est plutôt une question de sondage / liste. Et Stack Overflow n'a pas l'intention d'héberger ce genre de questions. Si vous n'êtes pas d'accord avec cette politique, vous voudrez peut-être en discuter sur meta .
Andrzej Doyle
7
Je suppose que la communauté n'est pas d'accord car cet article obtient plus de 100 vues / jour! Je l'ai trouvé très utile car je suis impliqué dans le développement d'un outil d'analyse statique spécialement conçu pour résoudre les problèmes de concurrence contemplateltd.com/threadsafe . Avoir une banque de problèmes de concurrence fréquemment rencontrés a été idéal pour tester et améliorer ThreadSafe.
Craig Manson
La liste de contrôle de révision de code pour Java Concurrency résume la plupart des pièges mentionnés dans les réponses à cette question sous une forme pratique pour les révisions de code au jour le jour.
leventov

Réponses:

125

Le problème de simultanéité le plus courant que j'ai vu n'est pas de réaliser qu'un champ écrit par un thread n'est pas garanti d'être vu par un thread différent. Une application courante de ceci:

class MyThread extends Thread {
  private boolean stop = false;

  public void run() {
    while(!stop) {
      doSomeWork();
    }
  }

  public void setStop() {
    this.stop = true;
  }
}

Tant que l'arrêt n'est pas volatile ou setStopet runn'est pas synchronisé, cela n'est pas garanti de fonctionner. Cette erreur est particulièrement diabolique, car dans 99,999%, cela n'aura pas d'importance en pratique car le fil du lecteur verra éventuellement le changement - mais nous ne savons pas combien de temps il l'a vu.

Kutzi
la source
9
Une excellente solution à cela est de faire de la variable d'instance d'arrêt un AtomicBoolean. Il résout tous les problèmes du non-volatile, tout en vous protégeant des problèmes JMM.
Kirk Wylie
39
C'est pire que «pendant plusieurs minutes» - vous pourriez ne JAMAIS le voir. Sous le modèle de mémoire, la JVM est autorisée à optimiser while (! Stop) en while (true) et ensuite vous êtes arrosé. Cela ne peut se produire que sur certaines VM, uniquement en mode serveur, uniquement lorsque la JVM se recompile après x itérations de la boucle, etc. Aïe!
Cowan
2
Pourquoi voudriez-vous utiliser AtomicBoolean sur un booléen volatil? Je développe pour la version 1.4+, alors y a-t-il des pièges à déclarer simplement volatile?
Piscine
2
Nick, je pense que c'est parce que le CAS atomique est généralement encore plus rapide que volatil. Si vous développez pour 1.4, votre seule option sûre à mon humble avis est d'utiliser synchronisé car volatile dans 1.4 n'a pas les garanties de barrière de mémoire forte comme il a dans Java 5.
Kutzi
5
@Thomas: c'est à cause du modèle de mémoire Java. Vous devriez le lire si vous voulez le connaître en détail (Java Concurrency in Practice par Brian Goetz l'explique bien par exemple). En bref: à moins que vous n'utilisiez des mots-clés / constructions de synchronisation de mémoire (comme volatile, synchronized, AtomicXyz, mais aussi lorsqu'un thread est terminé), un thread n'a AUCUNE garantie de voir les modifications apportées à n'importe quel champ effectué par un thread différent
Kutzi
179

Mon problème de concurrence le plus douloureux n ° 1 est survenu lorsque deux bibliothèques open source différentes ont fait quelque chose comme ceci:

private static final String LOCK = "LOCK";  // use matching strings 
                                            // in two different libraries

public doSomestuff() {
   synchronized(LOCK) {
       this.work();
   }
}

À première vue, cela ressemble à un exemple de synchronisation assez trivial. Toutefois; parce que les chaînes sont internées en Java, la chaîne littérale "LOCK"s'avère être la même instance de java.lang.String(même si elles sont déclarées de manière totalement disparate les unes des autres.) Le résultat est évidemment mauvais.

Jared
la source
63
C'est l'une des raisons pour lesquelles je préfère un objet final statique privé LOCK = new Object ();
Andrzej Doyle
17
Je l'aime - oh, c'est méchant :)
Thorbjørn Ravn Andersen
7
C'est un bon pour Java Puzzlers 2.
Dov Wasserman
12
En fait ... cela me donne vraiment envie que le compilateur refuse de vous permettre de vous synchroniser sur une chaîne. Étant donné le stage de String, il n'y a aucun cas où ce serait une "bonne chose (tm)".
Jared le
3
@Jared: "jusqu'à ce que la chaîne soit internée" n'a aucun sens. Les cordes ne "deviennent" pas internées par magie. String.intern () renvoie un objet différent, sauf si vous avez déjà l'instance canonique de la chaîne spécifiée. De plus, toutes les chaînes littérales et les expressions constantes à valeur chaîne sont internées. Toujours. Voir la documentation pour String.intern () et §3.10.5 du JLS.
Laurence Gonsalves
65

Un problème classique consiste à modifier l'objet sur lequel vous synchronisez lors de la synchronisation:

synchronized(foo) {
  foo = ...
}

D'autres threads simultanés se synchronisent alors sur un objet différent et ce bloc ne fournit pas l'exclusion mutuelle attendue.

Alex Miller
la source
19
Il y a une inspection IDEA pour cela appelé "Synchronisation sur un champ non final peu susceptible d'avoir une sémantique utile". Très agréable.
Jen S.20
8
Ha ... maintenant c'est une description torturée. "peu susceptible d'avoir une sémantique utile" pourrait être mieux décrit comme "probablement cassé". :)
Alex Miller
Je pense que c'était Bitter Java qui avait cela dans son ReadWriteLock. Heureusement, nous avons maintenant java.util.concurrency.locks, et Doug est un peu plus sur la balle.
Tom Hawtin - tackline
J'ai aussi souvent vu ce problème. Synchronisez uniquement sur les objets finaux, d'ailleurs. FindBugs et coll. aide, oui.
gimpf
est-ce seulement un problème lors de l'affectation? (Voir l'exemple de @Alex Miller ci-dessous avec une carte) Cet exemple de carte aurait-il également le même problème?
Alex Beardsley le
50

Un problème courant est l'utilisation de classes comme Calendar et SimpleDateFormat à partir de plusieurs threads (souvent en les mettant en cache dans une variable statique) sans synchronisation. Ces classes ne sont pas sûres pour les threads, donc l'accès multi-thread causera finalement des problèmes étranges avec un état incohérent.

Alex Miller
la source
Connaissez-vous un projet open source contenant ce bogue dans une version de celui-ci? Je recherche des exemples concrets de ce bogue dans un logiciel réel.
reprogrammeur
48

Ne se synchronise pas correctement sur les objets renvoyés par Collections.synchronizedXXX(), en particulier pendant l'itération ou plusieurs opérations:

Map<String, String> map = Collections.synchronizedMap(new HashMap<String, String>());

...

if(!map.containsKey("foo"))
    map.put("foo", "bar");

C'est faux . Malgré les opérations uniques synchronized, l'état de la carte entre les appels containset putpeut être modifié par un autre thread. Ça devrait être:

synchronized(map) {
    if(!map.containsKey("foo"))
        map.put("foo", "bar");
}

Ou avec une ConcurrentMapimplémentation:

map.putIfAbsent("foo", "bar");
Dave Ray
la source
6
Ou mieux, utilisez un ConcurrentHashMap et putIfAbsent.
Tom Hawtin - tackline
47

Verrouillage vérifié. Dans l'ensemble.

Le paradigme, dont j'ai commencé à apprendre les problèmes lorsque je travaillais au BEA, est que les gens vérifieront un singleton de la manière suivante:

public Class MySingleton {
  private static MySingleton s_instance;
  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) { s_instance = new MySingleton(); }
    }
    return s_instance;
  }
}

Cela ne fonctionne jamais, car un autre thread peut avoir pénétré dans le bloc synchronisé et s_instance n'est plus nulle. Le changement naturel est donc de le faire:

  public static MySingleton getInstance() {
    if(s_instance == null) {
      synchronized(MySingleton.class) {
        if(s_instance == null) s_instance = new MySingleton();
      }
    }
    return s_instance;
  }

Cela ne fonctionne pas non plus, car le modèle de mémoire Java ne le prend pas en charge. Vous devez déclarer s_instance comme volatile pour le faire fonctionner, et même dans ce cas, il ne fonctionne que sur Java 5.

Les personnes qui ne sont pas familières avec les subtilités du modèle de mémoire Java gâchent tout le temps .

Kirk Wylie
la source
7
Le modèle enum singleton résout tous ces problèmes (voir les commentaires de Josh Bloch à ce sujet). La connaissance de son existence devrait être plus répandue parmi les programmeurs Java.
Robin
Je n'ai pas encore rencontré un seul cas où l'initialisation paresseuse d'un singleton était réellement appropriée. Et si c'est le cas, déclarez simplement la méthode synchronisée.
Dov Wasserman le
3
C'est ce que j'utilise pour l'initialisation Lazy des classes Singleton. De plus, aucune synchronisation n'est requise car cela est garanti implicitement par java. class Foo {classe statique Holder {static Foo foo = new Foo (); } static Foo getInstance () {return Holder.foo; }}
Irfan Zulfiqar
Irfan, ça s'appelle la méthode de Pugh, d'après ce que je me souviens
Chris R
@Robin, n'est-il pas plus simple d'utiliser simplement un initialiseur statique? Ceux-ci sont toujours garantis pour fonctionner synchronisés.
matt b
37

Bien que probablement pas exactement ce que vous demandez, le problème le plus fréquent lié à la concurrence que j'ai rencontré (probablement parce qu'il survient dans un code à un seul thread normal) est un

java.util.ConcurrentModificationException

causé par des choses comme:

List<String> list = new ArrayList<String>(Arrays.asList("a", "b", "c"));
for (String string : list) { list.remove(string); }
Fabian Steeg
la source
Non, c'est totalement ce que je recherche. Merci!
Alex Miller
30

Il peut être facile de penser que les collections synchronisées vous accordent plus de protection qu'elles ne le font réellement et d'oublier de maintenir le verrou entre les appels. J'ai vu cette erreur à quelques reprises:

 List<String> l = Collections.synchronizedList(new ArrayList<String>());
 String[] s = l.toArray(new String[l.size()]);

Par exemple, dans la deuxième ligne ci-dessus, les méthodes toArray()et size()sont toutes deux thread-safe à part entière, mais le size()est évalué séparément de toArray(), et le verrou de la liste n'est pas maintenu entre ces deux appels.

Si vous exécutez ce code avec un autre thread en supprimant simultanément des éléments de la liste, tôt ou tard, vous vous retrouverez avec un nouveau String[]retourné qui est plus grand que nécessaire pour contenir tous les éléments de la liste et qui a des valeurs nulles dans la queue. Il est facile de penser que, puisque les deux appels de méthode à la liste se produisent dans une seule ligne de code, il s'agit en quelque sorte d'une opération atomique, mais ce n'est pas le cas.

Nick
la source
5
bon exemple. Je pense que je considérerais cela plus généralement comme "la composition des opérations atomiques n'est pas atomique". (Voir le champ volatile ++ pour un autre exemple simple)
Alex Miller
29

Le bogue le plus courant que nous voyons là où je travaille est que les programmeurs effectuent de longues opérations, comme des appels au serveur, sur l'EDT, verrouillant l'interface graphique pendant quelques secondes et rendant l'application insensible.

Eric Burke
la source
une de ces réponses pour lesquelles j'aimerais pouvoir donner plus d'un point
Epaga
2
EDT = Fil de discussion sur l'envoi d'événements
mjj1409
28

Oublier de wait () (ou Condition.await ()) dans une boucle, vérifier que la condition d'attente est réellement vraie. Sans cela, vous rencontrez des bogues dus à de faux réveils wait (). L'utilisation canonique doit être:

 synchronized (obj) {
     while (<condition does not hold>) {
         obj.wait();
     }
     // do stuff based on condition being true
 }
Alex Miller
la source
26

Un autre bogue courant est la mauvaise gestion des exceptions. Lorsqu'un thread d'arrière-plan lève une exception, si vous ne la gérez pas correctement, vous risquez de ne pas voir du tout la trace de la pile. Ou peut-être que votre tâche d'arrière-plan s'arrête et ne redémarre jamais parce que vous n'avez pas réussi à gérer l'exception.

Eric Burke
la source
Oui, et il existe de bons outils pour gérer cela maintenant avec les gestionnaires.
Alex Miller
3
Pourriez-vous publier des liens vers des articles ou des références qui expliquent cela plus en détail?
Abhijeet Kashnia
22

Jusqu'à ce que je prenne un cours avec Brian Goetz, je n'avais pas réalisé que le non-synchronisé getterd'un champ privé muté via un synchronisé settern'est jamais garanti pour retourner la valeur mise à jour. Ce n'est que lorsqu'une variable est protégée par un bloc synchronisé sur les deux lectures ET écritures que vous obtiendrez la garantie de la dernière valeur de la variable.

public class SomeClass{
    private Integer thing = 1;

    public synchronized void setThing(Integer thing)
        this.thing = thing;
    }

    /**
     * This may return 1 forever and ever no matter what is set
     * because the read is not synched
     */
    public Integer getThing(){
        return thing;  
    }
}
John Russell
la source
5
Dans les dernières JVM (1.5 et versions ultérieures, je pense), l'utilisation de volatile corrigera également ce problème.
James Schek
2
Pas nécessairement. volatile vous donne la dernière valeur donc il empêche le retour de 1 pour toujours, mais il ne fournit pas de verrouillage. C'est proche, mais pas tout à fait pareil.
John Russell
1
@JohnRussell Je pensais que volatile garantissait une relation d'avant. n'est-ce pas "verrouillage"? "Une écriture sur une variable volatile (§8.3.1.4) v se synchronise avec toutes les lectures ultérieures de v par n'importe quel thread (où la suite est définie selon l'ordre de synchronisation)."
Shawn
15

Pensant que vous écrivez du code à un seul thread, mais en utilisant des statiques mutables (y compris des singletons). Évidemment, ils seront partagés entre les threads. Cela arrive étonnamment souvent.

Tom Hawtin - Tacle
la source
3
Oui en effet! La statique mutable rompt le confinement du fil. Étonnamment, je n'ai jamais rien trouvé à propos de cet écueil, que ce soit dans JCiP ou CPJ.
Julien Chastang
J'espère que cela est évident pour les gens qui font de la programmation simultanée. L'état global devrait être le premier endroit pour vérifier la sécurité des threads.
gtrak
1
@Gary Thing, c'est qu'ils ne pensent pas faire de la programmation simultanée.
Tom Hawtin - ligne de pêche
15

Les appels de méthode arbitraires ne doivent pas être effectués à partir de blocs synchronisés.

Dave Ray en a parlé dans sa première réponse et, en fait, j'ai également rencontré une impasse liée à l'invocation de méthodes sur des écouteurs à partir d'une méthode synchronisée. Je pense que la leçon la plus générale est que les appels de méthode ne devraient pas être faits "dans la nature" à partir d'un bloc synchronisé - vous n'avez aucune idée si l'appel sera de longue durée, entraînera un blocage, ou autre.

Dans ce cas, et généralement en général, la solution était de réduire la portée du bloc synchronisé pour simplement protéger une section privée critique du code.

De plus, étant donné que nous accédions maintenant à la collection d'écouteurs en dehors d'un bloc synchronisé, nous l'avons changée en une collection copie sur écriture. Ou nous aurions pu simplement faire une copie défensive de la Collection. Le fait est qu'il existe généralement des alternatives pour accéder en toute sécurité à une collection d'objets inconnus.

Scott Bale
la source
13

Le bogue le plus récent lié à la concurrence que j'ai rencontré était un objet qui, dans son constructeur, créait un ExecutorService, mais lorsque l'objet n'était plus référencé, il n'avait jamais arrêté l'ExecutorService. Ainsi, sur une période de plusieurs semaines, des milliers de threads ont fui, provoquant finalement le crash du système. (Techniquement, il ne s'est pas écrasé, mais il a cessé de fonctionner correctement, tout en continuant à fonctionner.)

Techniquement, je suppose que ce n'est pas un problème de concurrence, mais c'est un problème lié à l'utilisation des bibliothèques java.util.concurrency.

Eddie
la source
11

Une synchronisation déséquilibrée, en particulier avec Maps, semble être un problème assez courant. Beaucoup de gens pensent qu'il suffit de synchroniser des mises à une carte (pas une ConcurrentMap, mais disons un HashMap) et de ne pas synchroniser sur des get. Cela peut cependant conduire à une boucle infinie lors du re-hachage.

Le même problème (synchronisation partielle) peut se produire partout où vous avez un état partagé avec des lectures et des écritures.

Alex Miller
la source
11

J'ai rencontré un problème de concurrence avec les servlets, lorsqu'il y a des champs mutables qui seront réglés à chaque demande. Mais il n'y a qu'une seule instance de servlet pour toutes les demandes, donc cela fonctionnait parfaitement dans un environnement à un seul utilisateur, mais lorsque plus d'un utilisateur demandait le servlet, des résultats imprévisibles se produisaient.

public class MyServlet implements Servlet{
    private Object something;

    public void service(ServletRequest request, ServletResponse response)
        throws ServletException, IOException{
        this.something = request.getAttribute("something");
        doSomething();
    }

    private void doSomething(){
        this.something ...
    }
}
Ludwig Wensauer
la source
10

Ce n'est pas exactement un bogue, mais le pire des péchés est de fournir une bibliothèque que vous souhaitez utiliser par d'autres personnes, mais de ne pas indiquer quelles classes / méthodes sont thread-safe et lesquelles ne doivent être appelées qu'à partir d'un seul thread, etc.

Davantage de personnes devraient utiliser les annotations de concurrence (par exemple @ThreadSafe, @GuardedBy, etc.) décrites dans le livre de Goetz.

Neil Bartlett
la source
9

Mon plus gros problème a toujours été les blocages, en particulier causés par les auditeurs qui sont tirés avec un verrou maintenu. Dans ces cas, il est très facile d'obtenir un verrouillage inversé entre deux threads. Dans mon cas, entre une simulation exécutée dans un thread et une visualisation de la simulation exécutée dans le thread UI.

EDIT: Déplacement de la deuxième partie pour séparer la réponse.

Dave Ray
la source
Pouvez-vous diviser la dernière en une réponse distincte? Gardons-le 1 par article. Ce sont deux très bons.
Alex Miller
9

Démarrer un thread dans le constructeur d'une classe est problématique. Si la classe est étendue, le thread peut être démarré avant que le constructeur de la sous-classe ne soit exécuté.

gris
la source
8

Classes mutables dans des structures de données partagées

Thread1:
    Person p = new Person("John");
    sharedMap.put("Key", p);
    assert(p.getName().equals("John");  // sometimes passes, sometimes fails

Thread2:
    Person p = sharedMap.get("Key");
    p.setName("Alfonso");

Lorsque cela se produit, le code est beaucoup plus complexe que cet exemple simplifié. Reproduire, trouver et corriger le bogue est difficile. Peut-être que cela pourrait être évité si nous pouvions marquer certaines classes comme immuables et certaines structures de données comme contenant uniquement des objets immuables.

Steve McLeod
la source
8

La synchronisation sur un littéral de chaîne ou une constante définie par un littéral de chaîne est (potentiellement) un problème car le littéral de chaîne est interné et sera partagé par toute autre personne dans la JVM utilisant le même littéral de chaîne. Je sais que ce problème est survenu dans les serveurs d'applications et d'autres scénarios de «conteneur».

Exemple:

private static final String SOMETHING = "foo";

synchronized(SOMETHING) {
   //
}

Dans ce cas, toute personne utilisant la chaîne «foo» pour verrouiller partage le même verrou.

Alex Miller
la source
Il est potentiellement verrouillé. Le problème est que la sémantique sur QUAND les chaînes sont internées n'est pas définie (ou, IMNSHO, sous-définie). Une constante de compilateur "foo" est internée, "foo" venant d'une interface réseau n'est interné que si vous le faites ainsi.
Kirk Wylie
C'est pourquoi j'ai spécifiquement utilisé une constante de chaîne littérale, qui est garantie d'être internée.
Alex Miller
8

Je crois qu'à l'avenir, le principal problème avec Java sera le (manque de) garanties de visibilité pour les constructeurs. Par exemple, si vous créez la classe suivante

class MyClass {
    public int a = 1;
}

puis lisez simplement la propriété de MyClass a à partir d'un autre thread, MyClass.a pourrait être 0 ou 1, selon l'implémentation et l'humeur de JavaVM. Aujourd'hui, les chances que «a» soit 1 sont très élevées. Mais sur les futures machines NUMA, cela peut être différent. Beaucoup de gens ne sont pas conscients de cela et pensent qu'ils n'ont pas besoin de se soucier du multi-threading pendant la phase d'initialisation.

Tim Jansen
la source
Je trouve cela légèrement surprenant, mais je sais que vous êtes un mec intelligent, Tim, alors je vais le prendre sans référence. :) Cependant, si un était définitif, ce ne serait pas un problème, n'est-ce pas? Vous seriez alors lié par la sémantique finale du gel lors de la construction?
Alex Miller
Je trouve encore des choses dans le JMM qui me surprennent, donc je ne me ferais pas confiance, mais j'en suis assez sûr. Voir aussi cs.umd.edu/~pugh/java/memoryModel/… . Si le champ était définitif, ce ne serait pas un problème, alors il serait visible après la phase d'initialisation.
Tim Jansen
2
Ce n'est un problème que si la référence de l'instance fraîchement créée est déjà utilisée avant que le constructeur ne soit retourné / terminé. Par exemple, la classe s'enregistre pendant la construction dans un pool public et d'autres threads commencent à y accéder.
ReneS
3
MyClass.a indique un accès statique et «a» n'est pas un membre statique de MyClass. En dehors de cela, c'est comme l'indique «ReneS», ce n'est un problème que si une référence à l'objet incomplet est divulguée, comme l'ajout de «ceci» à une carte externe dans le constructeur, par exemple.
Markus Jevring
7

L'erreur la plus stupide que je fais fréquemment est d'oublier de synchroniser avant d'appeler notify () ou wait () sur un objet.

Dave Ray
la source
8
Contrairement à la plupart des problèmes de concurrence, celui-ci n'est-il pas facile à trouver? Au moins, vous obtenez une exception IllegalMonitorStateException ici ...
Outlaw Programmer
Heureusement, il est très facile à trouver ... mais c'est toujours une erreur stupide qui me fait perdre plus de temps qu'il ne le devrait :)
Dave Ray
7

Utilisation d'un "nouvel objet ()" local comme mutex.

synchronized (new Object())
{
    System.out.println("sdfs");
}

Cela ne sert à rien.

Ludwig Wensauer
la source
2
C'est probablement inutile, mais l'acte de synchronisation fait des choses intéressantes ... Certes, créer un nouvel objet à chaque fois est un gaspillage complet.
TREE
4
Ce n'est pas inutile. C'est une barrière de mémoire sans verrou.
David Roussel
1
@David: le seul problème - jvm pourrait l'optimiser en supprimant du tout un tel verrou
yetanothercoder
@insighter Je vois que votre opinion est partagée ibm.com/developerworks/java/library/j-jtp10185/index.html Je suis d'accord que c'est une chose stupide à faire, car vous ne savez pas quand votre barrière de mémorisation se synchronisera, je faisait juste remarquer que cela faisait plus que rien.
David Roussel
7

Un autre problème courant de «concurrence» consiste à utiliser du code synchronisé lorsqu'il n'est pas du tout nécessaire. Par exemple, je vois encore des programmeurs utilisant StringBufferou même java.util.Vector(comme variables locales de méthode).

Kutzi
la source
1
Ce n'est pas un problème, mais inutile, car il indique à la JVM de synchroniser les données avec la mémoire globale et peut donc mal fonctionner sur plusieurs processeurs, même si personne n'utilise le bloc de synchronisation de manière simultanée.
ReneS
6

Plusieurs objets protégés par un verrou, mais auxquels on accède généralement les uns après les autres. Nous avons rencontré quelques cas où les verrous sont obtenus par un code différent dans des ordres différents, ce qui entraîne un blocage.

Brendan Cashman
la source
5

Ne pas se rendre compte que le thisdans une classe interne n'est pas le thisde la classe externe. Généralement dans une classe interne anonyme qui implémente Runnable. Le problème fondamental est que la synchronisation fait partie de toutObject s, il n'y a effectivement pas de vérification de type statique. J'ai vu cela au moins deux fois sur usenet, et cela apparaît également dans Brian Goetz'z Java Concurrency in Practice.

Les fermetures BGGA n'en souffrent pas car il n'y en a pas thispour la fermeture ( thisréférence la classe externe). Si vous utilisez des non- thisobjets comme verrous, cela permet de contourner ce problème et d'autres.

Tom Hawtin - Tacle
la source
3

Utilisation d'un objet global tel qu'une variable statique pour le verrouillage.

Cela conduit à de très mauvaises performances en raison de conflits.

kohlerm
la source
Eh bien, parfois, parfois non. Si c'était aussi simple que ça ...
gimpf
En supposant que le threading aide du tout à augmenter les performances pour le problème donné, il dégrade toujours les performances dès que plusieurs threads accèdent au code qui est protégé par le verrou.
kohlerm
3

Honesly? Avant l'avènement de java.util.concurrent, le problème le plus courant que je rencontrais régulièrement était ce que j'appelle "Thrash-Thrashing": les applications qui utilisent des threads pour la concurrence, mais en génèrent trop et finissent par se débattre.

Brian Clapper
la source
Voulez-vous dire que vous rencontrez plus de problèmes maintenant que java.util.concurrent est disponible?
Andrzej Doyle