Évitez de synchroniser (ceci) en Java?

Chaque fois qu’une question apparaît sur SO à propos de la synchronisation Java, certaines personnes sont très désireuses de signaler que cette synchronized(this) doit être évitée. Au lieu de cela, affirment-ils, un verrou sur une référence privée doit être préféré.

Certaines des raisons données sont:

  • un code maléfique peut voler votre verrou (très populaire celui-ci, a aussi une variante “accidentellement”)
  • toutes les méthodes synchronisées dans la même classe utilisent exactement le même verrou, ce qui réduit le débit
  • vous exposez (inutilement) trop d’informations

D’autres personnes, y compris moi-même, soutiennent que la synchronized(this) est un idiome très utilisé (également dans les bibliothèques Java), sûr et bien compris. Cela ne devrait pas être évité parce que vous avez un bogue et que vous n’avez pas la moindre idée de ce qui se passe dans votre programme multithread. En d’autres termes: s’il est applicable, alors utilisez-le.

Je suis intéressé à voir quelques exemples réels (pas de trucs foobar) où this est préférable d’éviter un verrou lorsque synchronized(this) ferait également le travail.

Par conséquent: devriez-vous toujours éviter la synchronized(this) et la remplacer par un verrou sur une référence privée?


Quelques informations supplémentaires (mises à jour au fur et à mesure des réponses):

  • nous parlons de synchronisation d’instance
  • les deux implicites (méthodes synchronized ) et les formes explicites de synchronized(this) sont considérées
  • Si vous citez Bloch ou d’autres autorités sur le sujet, ne laissez pas de côté les parties que vous n’aimez pas (par exemple, Java efficace, élément sur la sécurité des threads: il s’agit généralement du verrou sur l’instance, mais il y a des exceptions).
  • si vous avez besoin de granularité dans votre locking autre que synchronized(this) fournit, alors synchronized(this) n’est pas applicable, ce n’est pas le problème

    Je couvrirai chaque point séparément.

    1. Un code maléfique peut voler votre verrou (très populaire celui-ci, a aussi une variante “accidentellement”)

      Je suis plus préoccupé par accident . Cela revient à dire que cette utilisation fait partie de l’interface exposée de votre classe et doit être documentée. Parfois, la capacité d’un autre code à utiliser votre verrou est souhaitée. Cela est vrai pour des choses comme Collections.synchronizedMap (voir le javadoc).

    2. Toutes les méthodes synchronisées dans la même classe utilisent exactement le même verrou, ce qui réduit le débit

      C’est une pensée trop simpliste; se débarrasser de synchronized(this) ne résoudra pas le problème. Une synchronisation correcte pour le débit prendra plus de temps.

    3. Vous exposez (inutilement) trop d’informations

      Ceci est une variante de # 1. L’utilisation de synchronized(this) fait partie de votre interface. Si vous ne voulez pas / n’avez pas besoin de cela, ne le faites pas.

    Eh bien, premièrement, il faut souligner que:

     public void blah() { synchronized (this) { // do stuff } } 

    est sémantiquement équivalent à:

     public synchronized void blah() { // do stuff } 

    ce qui est une des raisons pour ne pas utiliser synchronized(this) . Vous pourriez soutenir que vous pouvez faire des choses autour du bloc synchronized(this) . La raison habituelle est d’essayer de ne pas avoir à effectuer le contrôle synchronisé, ce qui entraîne toutes sortes de problèmes de concurrence, en particulier le problème du double contrôle de locking , ce qui montre à quel point il est difficile de faire une vérification relativement simple. fil de sécurité.

    Une serrure privée est un mécanisme défensif, ce qui n’est jamais une mauvaise idée.

    De plus, comme vous l’avez mentionné, les verrous privés peuvent contrôler la granularité. Un ensemble d’opérations sur un object peut être totalement indépendant d’un autre, mais synchronized(this) exclura mutuellement l’access à tous.

    synchronized(this) ne vous donne rien.

    Lorsque vous utilisez synchronized (this), vous utilisez l’instance de classe en tant que verrou lui-même. Cela signifie que tant que le locking est acquis par le thread 1, le thread 2 doit attendre

    Supposons le code suivant

     public void method1() { do something ... synchronized(this) { a ++; } ................ } public void method2() { do something ... synchronized(this) { b ++; } ................ } 

    La méthode 1 modifiant la variable a et la méthode 2 modifiant la variable b , la modification simultanée de la même variable par deux threads devrait être évitée et elle l’est. MAIS pendant que thread1 modifie un et thread2 modifiant b il peut être effectué sans aucune condition de course.

    Malheureusement, le code ci-dessus ne le permet pas car nous utilisons la même référence pour un verrou; Cela signifie que les threads, même s’ils ne sont pas dans une situation de concurrence, doivent attendre et que le code sacrifie évidemment la concurrence du programme.

    La solution consiste à utiliser 2 verrous différents pour deux variables différentes.

      class Test { private Object lockA = new Object(); private Object lockB = new Object(); public void method1() { do something ... synchronized(lockA) { a ++; } ................ } public void method2() { do something ... synchronized(lockB) { b ++; } ................ } 

    L’exemple ci-dessus utilise plus de verrous à granularité fine (2 verrous à la place 1 ( lockA et lockB pour les variables a et b respectivement), ce qui permet une meilleure concurrence, mais devient plus complexe que le premier exemple …

    Bien que je sois d’accord sur le fait de ne pas adhérer aveuglément aux règles dogmatiques, le scénario du “vol du verrou” vous semble-t-il si excensortingque? Un thread pourrait en effet acquérir le verrou sur votre object “en externe” ( synchronized(theObject) {...} ), bloquant les autres threads en attente de méthodes d’instance synchronisées.

    Si vous ne croyez pas au code malveillant, considérez que ce code peut provenir de tiers (par exemple, si vous développez une sorte de serveur d’application).

    La version “accidentelle” semble moins probable, mais comme on dit, “faire quelque chose d’improbable et que quelqu’un inventera un meilleur idiot”.

    Donc, je suis d’accord avec le principe de la pensée «ça dépend de ce que fait la classe».


    Modifier les 3 premiers commentaires d’eljenso suivants:

    Je n’ai jamais connu le problème du vol de verrou mais voici un scénario imaginaire:

    Disons que votre système est un conteneur de servlets et que l’object que nous considérons est l’implémentation de ServletContext . Sa méthode getAtsortingbute doit être thread-safe, car les atsortingbuts de contexte sont des données partagées; vous le déclarez donc comme synchronized . Imaginons également que vous fournissiez un service d’hébergement public basé sur votre implémentation de conteneur.

    Je suis votre client et déploie mon “bon” servlet sur votre site. Il se trouve que mon code contient un appel à getAtsortingbute .

    Un pirate, déguisé en un autre client, déploie son servlet malveillant sur votre site. Il contient le code suivant dans la méthode init :

     synchronisé (this.getServletConfig (). getServletContext ()) {
        while (true) {}
     }
    

    En supposant que nous partageons le même contexte de servlet (autorisé par la spécification tant que les deux servlets sont sur le même hôte virtuel), mon appel sur getAtsortingbute est verrouillé pour toujours. Le pirate a réalisé un DoS sur mon servlet.

    Cette attaque n’est pas possible si getAtsortingbute est synchronisé sur un verrou privé, car le code tiers ne peut pas acquérir ce verrou.

    J’admets que l’exemple est artificiel et une vision simpliste du fonctionnement d’un conteneur de servlets, mais à mon humble avis, cela prouve le point.

    Je ferais donc mon choix de conception en tenant compte de la sécurité: aurai-je un contrôle complet sur le code qui a access aux instances? Quelle serait la conséquence d’un thread tenant un verrou sur une instance indéfiniment?

    Il semble y avoir un consensus différent dans les camps C # et Java à ce sujet. La majorité du code Java que j’ai vu utilise:

     // apply mutex to this instance synchronized(this) { // do work here } 

    alors que la majorité du code C # opte pour le plus sûr sans doute:

     // instance level lock object private readonly object _syncObj = new object(); ... // apply mutex to private instance level field (a System.Object usually) lock(_syncObj) { // do work here } 

    Le langage C # est certainement plus sûr. Comme mentionné précédemment, aucun access malveillant / accidentel au verrou ne peut être effectué depuis l’extérieur de l’instance. Le code Java présente également ce risque, mais il semble que la communauté Java a progressivement évolué vers la version légèrement moins sûre, mais légèrement plus concise.

    Cela ne veut pas dire une recherche contre Java, juste à la lumière de mon expérience de travail sur les deux langues.

    Ça dépend de la situation.
    S’il n’y a qu’une entité de partage ou plus d’une.

    Voir exemple de travail complet ici

    Une petite introduction

    Threads et entités partageables
    Il est possible que plusieurs threads accèdent à la même entité, par exemple, plusieurs connectionsThreads partageant un seul messageQueue. Comme les threads s’exécutent simultanément, il peut y avoir une chance de remplacer ses données par une autre, ce qui peut être une erreur.
    Nous avons donc besoin d’un moyen de nous assurer que l’entité partageable n’est accessible que par un seul thread à la fois (CONCURRENCE).

    Bloc synchronisé
    Le bloc synchronized () est un moyen de garantir un access simultané à une entité partageable.
    Tout d’abord, une petite analogie
    Supposons Il y a deux personnes P1, P2 (fils) un lavabo (entité partageable) à l’intérieur d’une salle de bain et il y a porte (serrure).
    Maintenant, nous voulons qu’une personne utilise le lavabo à la fois.
    L’approche consiste à verrouiller la porte par P1, lorsque la porte est verrouillée, P2 attend que p1 termine son travail
    P1 déverrouille la porte
    alors seul p1 peut utiliser le lavabo.

    syntaxe.

     synchronized(this) { SHARED_ENTITY..... } 

    “this” fournissait le verrou insortingnsèque associé à la classe (la classe Object conçue par le développeur Java de manière à ce que chaque object puisse fonctionner en tant que moniteur). L’approche ci-dessus fonctionne bien lorsqu’il n’y a qu’une seule entité partagée et plusieurs threads (1: N).
    entrer la description de l'image ici N entités partageables-M threads
    Pensez maintenant à une situation où il y a deux lavabos à l’intérieur d’une canvastte et une seule porte. Si nous utilisons l’approche précédente, seul p1 peut utiliser un seul lavabo à la fois alors que p2 attendra à l’extérieur. C’est le gaspillage de ressources car personne n’utilise B2 (lavabo).
    Une approche plus sage serait de créer des pièces plus petites à l’intérieur de la salle de bain et de leur fournir une porte par lavabo. De cette manière, P1 peut accéder à B1 et P2 peut accéder à B2 et vice-versa.

     washbasin1; washbasin2; Object lock1=new Object(); Object lock2=new Object(); synchronized(lock1) { washbasin1; } synchronized(lock2) { washbasin2; } 

    entrer la description de l'image ici
    entrer la description de l'image ici

    Voir plus sur Threads —-> ici

    Le package java.util.concurrent a considérablement réduit la complexité du code de sécurité de mon thread. Je n’ai que des preuves anecdotiques à suivre, mais la plupart des travaux que j’ai vus avec synchronized(x) semblent être la ré-implémentation d’un Lock, d’un Sémaphore ou d’un Latch, mais en utilisant les moniteurs de niveau inférieur.

    En gardant cela à l’esprit, la synchronisation à l’aide de l’un de ces mécanismes est analogue à la synchronisation sur un object interne, plutôt que de la fuite d’un verrou. Cela est bénéfique car vous avez la certitude absolue que vous contrôlez l’entrée dans le moniteur par plusieurs threads.

    Si vous avez décidé que:

    • la chose à faire est de verrouiller l’object en cours; et
    • vous voulez le verrouiller avec une granularité inférieure à une méthode entière;

    alors je ne vois pas le tabou sur synchronizezd (this).

    Certaines personnes utilisent délibérément synchronisé (this) (au lieu de marquer la méthode synchronisée) dans tout le contenu d’une méthode, car ils pensent qu’il est “plus clair pour le lecteur” quel object est actuellement synchronisé. Tant que les gens font un choix éclairé (par exemple, comprendre que ce faisant, ils insèrent réellement des bytecodes supplémentaires dans la méthode et que cela pourrait avoir un effet sur les optimisations potentielles), je ne vois pas particulièrement de problème avec ceci. . Vous devez toujours documenter le comportement simultané de votre programme. Je ne vois donc pas l’argument “‘synchronisé’ qui publie le comportement” comme très convaincant.

    En ce qui concerne la question de savoir quel verrou d’object vous devez utiliser, je pense qu’il n’y a rien de mal à synchroniser l’object actuel si cela est attendu par la logique de ce que vous faites et comment votre classe est généralement utilisée . Par exemple, avec une collection, l’object que vous souhaitez logiquement verrouiller est généralement la collection elle-même.

    1. Rendez vos données immuables si possible (variables final )
    2. Si vous ne pouvez pas éviter la mutation des données partagées sur plusieurs threads, utilisez des constructions de programmation de haut niveau [par exemple, API de Lock granulaire]

    Un verrou fournit un access exclusif à une ressource partagée: un seul thread à la fois peut acquérir le verrou et tout access à la ressource partagée nécessite l’acquisition du verrou en premier.

    Exemple de code pour utiliser ReentrantLock qui implémente l’interface Lock

      class X { private final ReentrantLock lock = new ReentrantLock(); // ... public void m() { lock.lock(); // block until condition holds try { // ... method body } finally { lock.unlock() } } } 

    Avantages de Lock over Synchronized (this)

    1. L’utilisation de méthodes ou d’énoncés synchronisés force toutes les acquisitions et libérations de verrous à se produire de manière structurée en blocs.

    2. Les implémentations de locking offrent des fonctionnalités supplémentaires par rapport à l’utilisation de méthodes et d’instructions synchronisées en fournissant

      1. Une tentative non bloquante d’acquérir un verrou ( tryLock() )
      2. Une tentative d’acquisition du verrou pouvant être interrompu ( lockInterruptibly() )
      3. Une tentative pour acquérir le verrou qui peut tryLock(long, TimeUnit) ).
    3. Une classe Lock peut également fournir un comportement et une sémantique assez différents de ceux du verrou de moniteur implicite, tels que

      1. commande garantie
      2. usage non-rentrant
      3. Détection de blocage

    Jetez un coup d’œil à cette question du SE concernant divers types de Locks :

    Synchronisation vs locking

    Vous pouvez atteindre la sécurité des threads en utilisant une API de concurrence avancée au lieu des blocs synchronisés. Cette page de documentation fournit de bonnes constructions de programmation pour assurer la sécurité des threads.

    Les objects de locking prennent en charge les idiomes de locking qui simplifient de nombreuses applications simultanées.

    Les exécuteurs définissent une API de haut niveau pour le lancement et la gestion des threads. Les implémentations exécutables fournies par java.util.concurrent fournissent une gestion des pools de threads adaptée aux applications à grande échelle.

    Les collections simultanées facilitent la gestion de grandes collections de données et peuvent réduire considérablement le besoin de synchronisation.

    Les variables atomiques ont des fonctionnalités qui minimisent la synchronisation et aident à éviter les erreurs de cohérence de la mémoire.

    ThreadLocalRandom (dans JDK 7) fournit une génération efficace de nombres pseudo-aléatoires à partir de plusieurs threads.

    Reportez-vous également aux packages java.util.concurrent et java.util.concurrent.atomic pour les autres constructions de programmation.

    Je pense qu’il y a une bonne explication sur la raison pour laquelle chacune de ces techniques est essentielle dans un livre intitulé Java Concurrency In Practice de Brian Goetz. Il met un point très clair: vous devez utiliser le même verrou “PARTOUT” pour protéger l’état de votre object. La méthode synchronisée et la synchronisation sur un object vont souvent de pair. Par exemple, Vector synchronise toutes ses méthodes. Si vous avez un handle vers un object vectoriel et que vous allez faire “put if absent”, alors la simple synchronisation Vectorielle de ses propres méthodes individuelles ne vous protégera pas de la corruption de l’état. Vous devez synchroniser en utilisant synchronized (vectorHandle). Cela aura pour résultat que le verrou SAME sera acquis par chaque thread qui a un handle vers le vecteur et protégera l’état global du vecteur. Cela s’appelle le locking côté client. Nous soaps qu’un vecteur de fait synchronise (ceci) / synchronise toutes ses méthodes et donc la synchronisation sur l’object vectorHandle se traduira par une synchronisation correcte de l’état des objects vectoriels. Il est stupide de croire que vous êtes thread-safe juste parce que vous utilisez une collection thread-safe. C’est précisément la raison pour laquelle ConcurrentHashMap a explicitement introduit la méthode putIfAbsent – pour rendre ces opérations atomiques.

    En résumé

    1. La synchronisation au niveau de la méthode permet le locking côté client.
    2. Si vous avez un object de locking privé, cela rend impossible le locking côté client. Cela ne pose aucun problème si vous savez que votre classe n’a pas le type de fonctionnalité “put if absent”.
    3. Si vous concevez une bibliothèque, la synchronisation sur cette méthode ou la synchronisation de la méthode est souvent plus judicieuse. Parce que vous êtes rarement en mesure de décider comment votre classe va être utilisée.
    4. Si Vector avait utilisé un object de locking privé, il aurait été impossible d’obtenir “put if absent” à droite. Le code client ne sera jamais pris en charge par le verrou privé, ce qui irait à l’encontre de la règle fondamentale consistant à utiliser EXACT SAME LOCK pour protéger son état.
    5. La synchronisation sur cette méthode ou sur des méthodes synchronisées pose problème, comme d’autres l’ont fait remarquer: quelqu’un pourrait obtenir un verrou et ne jamais le libérer. Tous les autres threads continueraient d’attendre que le verrou soit libéré.
    6. Alors, sachez ce que vous faites et adoptez celui qui est correct.
    7. Quelqu’un a fait valoir que le fait d’avoir un object de locking privé vous offre une meilleure granularité – par exemple, si deux opérations ne sont pas liées – elles peuvent être protégées par différents verrous, ce qui améliore le débit. Mais je pense qu’il s’agit d’une odeur de conception et non d’une odeur de code – si deux opérations n’ont aucun rapport, pourquoi font-elles partie de la même classe? Pourquoi un club de classe devrait-il avoir des fonctionnalités indépendantes? Peut être une classe utilitaire? Hmmmm – certains util fournissant la manipulation de chaîne et le formatage de date du calendrier à travers la même instance ?? … n’a aucun sens pour moi au moins !!

    Non, vous ne devriez pas toujours . Cependant, j’ai tendance à l’éviter quand il y a de multiples préoccupations sur un object particulier qui ne doivent être sécurisées que par rapport à elles-mêmes. Par exemple, vous pouvez avoir un object de données mutable avec des champs “label” et “parent”; ceux-ci doivent être threadsafe, mais le changement de l’un ne doit pas empêcher l’autre d’écrire / lire. (En pratique, j’éviterais cela en déclarant les champs volatiles et / ou en utilisant les wrappers jomans.util.concurrent AtomicFoo).

    La synchronisation en général est un peu maladroite, car elle bloque un gros verrou plutôt que de penser exactement comment les threads peuvent se contourner. L’utilisation de synchronized(this) est encore plus maladroite et antisociale, car elle dit “personne ne peut rien changer à cette classe pendant que je tiens le verrou”. À quelle fréquence avez-vous réellement besoin de le faire?

    Je préférerais beaucoup plus de serrures granulaires; même si vous voulez tout empêcher de changer (peut-être que vous sérialisez l’object), vous pouvez simplement acquérir tous les verrous pour obtenir la même chose, et c’est plus explicite de cette manière. Lorsque vous utilisez la synchronized(this) , vous ne savez pas exactement pourquoi vous synchronisez ou quels sont les effets secondaires possibles. Si vous utilisez synchronized(labelMonitor) , ou mieux encore labelLock.getWriteLock().lock() , vous savez clairement ce que vous faites et quels sont les effets de votre section critique.

    Réponse courte : Vous devez comprendre la différence et faire un choix en fonction du code.

    Réponse longue : En général, je préférerais éviter la synchronisation (this) pour réduire les conflits, mais les verrous privés compliquent la tâche. Utilisez donc la bonne synchronisation pour le bon travail. Si vous n’êtes pas si expérimenté avec la programmation multi-thread, je préfère m’en tenir au locking des instances et lire ce sujet. (Cela dit: utiliser simplement synchronize (this) ne rend pas automatiquement votre classe complètement compatible avec les threads). Ce n’est pas un sujet facile mais une fois que vous vous y êtes habitué, la réponse est d’utiliser naturellement ou non .

    Un verrou est utilisé pour la visibilité ou pour protéger certaines données contre des modifications simultanées pouvant conduire à la course.

    Lorsque vous devez simplement effectuer des opérations de type primitif pour être atomiques, il existe des options telles que AtomicInteger et les similaires.

    Mais supposons que vous ayez deux entiers liés les uns aux autres, comme les x et y , qui sont liés entre eux et doivent être modifiés de manière atomique. Ensuite, vous les protégeriez en utilisant le même verrou.

    Un verrou ne doit protéger que l’état lié l’un à l’autre. Pas moins et pas plus. Si vous utilisez synchronized(this) dans chaque méthode, même si l’état de la classe n’est pas lié, tous les threads seront confrontés à des conflits même s’ils mettent à jour leur état sans rapport.

     class Point{ private int x; private int y; public Point(int x, int y){ this.x = x; this.y = y; } //mutating methods should be guarded by same lock public synchronized void changeCoordinates(int x, int y){ this.x = x; this.y = y; } } 

    Dans l’exemple ci-dessus, je n’ai qu’une méthode qui mute à la fois x et y et non deux méthodes différentes comme x et y sont liées et si j’avais donné deux méthodes différentes pour muter x et y séparément,

    Cet exemple est juste pour démontrer et pas nécessairement la façon dont il devrait être mis en œuvre. La meilleure façon de le faire serait de le rendre IMMUTABLE .

    Maintenant, contrairement à l’exemple de Point , il existe un exemple de TwoCounters déjà fourni par @Andreas où l’état qui est protégé par deux verrous différents comme l’état n’est pas lié les uns aux autres.

    Le processus d’utilisation de différents verrous pour protéger des états non liés est appelé Locking Ssortingping ou Lock Splitting.

    La raison pour ne pas synchroniser sur ceci est que parfois vous avez besoin de plus d’un verrou (le second verrou est souvent supprimé après quelques reflections supplémentaires, mais vous en avez toujours besoin dans l’état intermédiaire). Si vous verrouillez ceci , vous devez toujours vous rappeler lequel des deux verrous est celui-ci ; Si vous verrouillez un object privé, le nom de la variable vous le dit.

    Du sharepoint vue du lecteur, si vous voyez cela verrouillé, vous devez toujours répondre aux deux questions suivantes:

    1. quel type d’access est protégé par cela ?
    2. est-ce qu’un verrou est vraiment suffisant, quelqu’un n’a-t-il pas introduit un bug?

    Un exemple:

     class BadObject { private Something mStuff; synchronized setStuff(Something stuff) { mStuff = stuff; } synchronized getStuff(Something stuff) { return mStuff; } private MyListener myListener = new MyListener() { public void onMyEvent(...) { setStuff(...); } } synchronized void longOperation(MyListener l) { ... l.onMyEvent(...); ... } } 

    Si deux threads commencent longOperation() sur deux instances différentes de BadObject , ils acquièrent leurs verrous; quand il est temps d’appeler l.onMyEvent(...) , nous avons un blocage car aucun des threads ne peut acquérir le verrou de l’autre object.

    Dans cet exemple, nous pouvons éliminer le blocage en utilisant deux verrous, un pour les opérations courtes et un pour les opérations longues.

    Comme déjà dit ici, le bloc synchronisé peut utiliser une variable définie par l’utilisateur comme object de locking, lorsque la fonction synchronisée utilise uniquement “this”. Et bien sûr, vous pouvez manipuler les zones de votre fonction qui doivent être synchronisées, etc.

    Mais tout le monde dit qu’il n’y a pas de différence entre la fonction synchronisée et le bloc qui couvre toute la fonction en utilisant “this” comme object de locking. Ce n’est pas vrai, la différence réside dans le code d’octet qui sera généré dans les deux situations. En cas de blocage synchronisé, il convient d’atsortingbuer une variable locale à «this». Et comme résultat, nous aurons un peu plus de taille de fonction (ce qui n’est pas pertinent si vous n’avez que peu de fonctions).

    Une explication plus détaillée de la différence que vous pouvez trouver ici: http://www.artima.com/insidejvm/ed2/threadsynchP.html

    L’utilisation du bloc synchronisé n’est pas non plus bonne en raison du sharepoint vue suivant:

    Le mot-clé synchronisé est très limité dans une zone: en quittant un bloc synchronisé, tous les threads en attente de ce verrou doivent être débloqués, mais seul l’un de ces threads peut prendre le verrou; tous les autres voient que la serrure est prise et reviennent à l’état bloqué. Ce ne sont pas que de nombreux cycles de traitement gaspillés: souvent, le changement de contexte pour débloquer un thread implique également de paginer la mémoire sur le disque, ce qui est très, très cher.

    Pour plus de détails dans ce domaine, je vous recommande de lire cet article: http://java.dzone.com/articles/synchronized-considered

    Un bon exemple d’utilisation synchronisée (this).

     // add listener public final synchronized void addListener(IListener l) {listeners.add(l);} // remove listener public final synchronized void removeListener(IListener l) {listeners.remove(l);} // routine that raise events public void run() { // some code here... Set ls; synchronized(this) { ls = listeners.clone(); } for (IListener l : ls) { l.processEvent(event); } // some code here... } 

    As you can see here, we use synchronize on this to easy cooperate of lengthly (possibly infinite loop of run method) with some synchronized methods there.

    Of course it can be very easily rewritten with using synchronized on private field. But sometimes, when we already have some design with synchronized methods (ie legacy class, we derive from, synchronized(this) can be the only solution).

    It depends on the task you want to do, but I wouldn’t use it. Also, check if the thread-save-ness you want to accompish couldn’t be done by synchronize(this) in the first place? There are also some nice locks in the API that might help you 🙂

    I think points one (somebody else using your lock) and two (all methods using the same lock needlessly) can happen in any fairly large application. Especially when there’s no good communication between developers.

    It’s not cast in stone, it’s mostly an issue of good practice and preventing errors.