Chaque fois qu'une question surgit sur SO à propos de la synchronisation Java, certaines personnes sont très désireuses de souligner que cela synchronized(this)
devrait être évité. 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 serrure (très populaire, celui-ci a également 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, dont moi, soutiennent que synchronized(this)
c'est un idiome qui est beaucoup utilisé (également dans les bibliothèques Java), est sûr et bien compris. Il ne faut pas l'éviter car vous avez un bug et 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, utilisez-le.
Je suis intéressé à voir des exemples du monde réel (pas de trucs de foobar) où éviter un verrouillage this
est préférable quand synchronized(this)
ferait également le travail.
Par conséquent: devez-vous toujours l'éviter synchronized(this)
et le 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
synchronized
formes implicites ( méthodes) et explicites desynchronized(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 elle-même, mais il y a des exceptions.)
- si vous avez besoin d'une granularité dans votre verrouillage autre que celle
synchronized(this)
fournie, alors cesynchronized(this)
n'est pas applicable donc ce n'est pas le problème
Réponses:
Je couvrirai chaque point séparément.
Je suis plus inquiet par accident . Cela revient à dire que cette utilisation de
this
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 commeCollections.synchronizedMap
(voir le javadoc).C'est une pensée trop simpliste; se débarrasser de
synchronized(this)
ne résoudra pas le problème. Une synchronisation appropriée pour le débit demandera plus de réflexion.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 cette exposition, ne le faites pas.la source
keySet()
etvalues()
ne se verrouillent pas sur (leur)this
, mais l'instance de carte, qui est importante pour obtenir un comportement cohérent pour toutes les opérations de carte. La raison pour laquelle l'objet verrou est pris en compte dans une variable est que la sous-classe en aSynchronizedSortedMap
besoin pour implémenter des sous-cartes qui se verrouillent sur l'instance de carte d'origine.Eh bien, tout d'abord, il convient de souligner que:
est sémantiquement équivalent à:
ce qui est une raison de ne pas l'utiliser
synchronized(this)
. Vous pourriez faire valoir que vous pouvez faire des choses autour dusynchronized(this)
bloc. La raison habituelle est d'essayer et d'éviter d'avoir à faire la vérification synchronisée, ce qui entraîne toutes sortes de problèmes de concurrence, en particulier le problème de verrouillage à double vérification , qui montre à quel point il peut être difficile de faire une vérification relativement simple threadsafe.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 objet peut être totalement indépendant d'un autre mais
synchronized(this)
exclura mutuellement l'accès à tous.synchronized(this)
ne vous donne vraiment rien.la source
Lorsque vous utilisez synchronisé (ceci), vous utilisez l'instance de classe comme verrou lui-même. Cela signifie que si le verrouillage est acquis par le thread 1 , le thread 2 doit attendre.
Supposons que le code suivant:
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 doit être évitée et elle l'est. MAIS tout en modifiant thread1 a et en modifiant thread2 b, cela peut être effectué sans condition de concurrence .
Malheureusement, le code ci-dessus ne le permettra 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 condition de concurrence critique, doivent attendre et, évidemment, le code sacrifie la concurrence du programme.
La solution consiste à utiliser 2 verrous différents pour deux variables différentes:
L'exemple ci-dessus utilise des verrous plus fins (2 verrous au lieu d'un ( lockA et lockB pour les variables a et b respectivement) et, par conséquent, permet une meilleure concurrence, en revanche, il est devenu plus complexe que le premier exemple ...
la source
Bien que je convienne de ne pas adhérer aveuglément aux règles dogmatiques, le scénario de "vol de serrure" vous semble-t-il si excentrique? Un thread pourrait en effet acquérir le verrouillage de votre objet "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'applications).
La version "accidentelle" semble moins probable, mais comme on dit, "faites quelque chose d'idiot-proof et quelqu'un inventera un meilleur idiot".
Je suis donc d'accord avec l'école de pensée qui dépend de ce que la classe fait.
Modifier les 3 premiers commentaires d'eljenso suivants:
Je n'ai jamais rencontré de problème de vol de serrure mais voici un scénario imaginaire:
Supposons que votre système soit un conteneur de servlet et que nous considérions l'
ServletContext
implémentation. SagetAttribute
méthode doit être thread-safe, car les attributs de contexte sont des données partagées; vous le déclarez doncsynchronized
. Imaginons également que vous fournissiez un service d'hébergement public basé sur l'implémentation de votre conteneur.Je suis votre client et déploie ma "bonne" servlet sur votre site. Il arrive que mon code contienne un appel à
getAttribute
.Un pirate informatique, déguisé en un autre client, déploie son servlet malveillant sur votre site. Il contient le code suivant dans la
init
méthode: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
getAttribute
est verrouillé pour toujours. Le pirate a réalisé un DoS sur ma servlet.Cette attaque n'est pas possible si elle
getAttribute
est synchronisée sur un verrou privé, car le code tiers ne peut pas acquérir ce verrou.J'admets que l'exemple est artificiel et une vue trop simpliste du fonctionnement d'un conteneur de servlet, mais à mon humble avis, cela prouve le point.
Je ferais donc mon choix de conception en fonction de la sécurité: vais-je avoir un contrôle complet sur le code qui a accès aux instances? Quelle serait la conséquence d'un thread qui détient un verrou sur une instance indéfiniment?
la source
Ça dépend de la situation.
S'il n'y a qu'une seule entité de partage ou plus d'une.
Voir l'exemple de travail complet ici
Une petite introduction.
Threads et entités partageables
Il est possible pour plusieurs threads d'accéder à la même entité, par exemple pour plusieurs connexionsThreads partageant un même messageQueue. Étant donné que les threads s'exécutent simultanément, il peut y avoir une possibilité de remplacer ses données par une autre, ce qui peut être une situation foireuse.
Nous avons donc besoin d'un moyen de garantir que l'entité partageable n'est accessible que par un thread à la fois. (CONCURRENCE).
Bloc
synchronisé Le bloc synchronized () est un moyen d'assurer un accès simultané à une entité partageable.
Tout d'abord, une petite analogie
Supposons qu'il y ait deux personnes P1, P2 (fils) un lavabo (entité partageable) à l'intérieur d'une salle de bain et qu'il y ait une porte (serrure).
Maintenant, nous voulons qu'une personne utilise le lavabo à la fois.
Une approche consiste à verrouiller la porte par P1 lorsque la porte est verrouillée P2 attend jusqu'à ce que p1 termine son travail
P1 déverrouille la porte
puis seul p1 peut utiliser le lavabo.
syntaxe.
"this" fournissait le verrou intrinsèque associé à la classe (le développeur Java a conçu la classe Object de telle manière que chaque objet puisse fonctionner comme moniteur). L'approche ci-dessus fonctionne correctement lorsqu'il n'y a qu'une seule entité partagée et plusieurs threads (1: N). N entités partageables-fils M Imaginez maintenant une situation où il y a deux lavabos à l'intérieur d'une salle de bain et une seule porte. Si nous utilisons l'approche précédente, seul p1 peut utiliser un seul lavabo à la fois tandis que p2 attendra dehors. C'est un gaspillage de ressources car personne n'utilise B2 (lavabo). Une approche plus sage serait de créer une pièce plus petite à l'intérieur des toilettes et de leur fournir une porte par lavabo. De cette façon, P1 peut accéder à B1 et P2 peut accéder à B2 et vice-versa.
En savoir plus sur les discussions ----> ici
la source
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:
alors que la majorité du code C # opte pour le sans doute plus sûr:
L'idiome C # est certainement plus sûr. Comme mentionné précédemment, aucun accès 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 ait évolué au fil du temps vers la version légèrement moins sûre, mais légèrement plus laconique.
Cela ne signifie pas une fouille contre Java, juste un reflet de mon expérience de travail sur les deux langages.
la source
Le
java.util.concurrent
package a considérablement réduit la complexité de mon code thread-safe. Je n'ai que des preuves anecdotiques pour continuer, mais la plupart des travaux que j'ai vussynchronized(x)
semblent réimplémenter un verrou, un sémaphore ou un verrou, mais en utilisant les moniteurs de niveau inférieur.Dans cet esprit, la synchronisation à l'aide de l'un de ces mécanismes est analogue à la synchronisation sur un objet interne, plutôt que de laisser échapper un verrou. Ceci est avantageux dans la mesure où vous avez la certitude absolue que vous contrôlez l'entrée dans le moniteur par deux threads ou plus.
la source
final
variables)Lock
API granulaire ]Exemple de code à utiliser
ReentrantLock
qui implémente l'Lock
interfaceAvantages de Lock over Synchronized (ceci)
L'utilisation de méthodes ou d'instructions synchronisées oblige toutes les acquisitions et libérations de verrous à se produire de manière structurée par blocs.
Les implémentations de verrouillage offrent des fonctionnalités supplémentaires par rapport à l'utilisation de méthodes et d'instructions synchronisées en fournissant
tryLock()
)lockInterruptibly()
)tryLock(long, TimeUnit)
).Une classe Lock peut également fournir un comportement et une sémantique très différents de ceux du verrouillage implicite du moniteur, tels que
Jetez un oeil à cette question SE concernant différents types de
Locks
:Synchronisation vs verrouillage
Vous pouvez garantir la sécurité des threads en utilisant une API de concurrence avancée au lieu de blocs synchronisés. Cette page de documentation fournit de bonnes constructions de programmation pour assurer la sécurité des threads.
Les objets de verrouillage prennent en charge les idiomes de verrouillage qui simplifient de nombreuses applications simultanées.
Les exécuteurs définissent une API de haut niveau pour lancer et gérer les threads. Les implémentations d'exécuteur fournies par java.util.concurrent fournissent une gestion de pool de threads adaptée aux applications à grande échelle.
Les collections simultanées facilitent la gestion de grandes collections de données et peuvent considérablement réduire 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 d'autres constructions de programmation.
la source
Si vous avez décidé que:
alors je ne vois pas le tabou sur synchronizezd (ceci).
Certaines personnes utilisent délibérément synchronisé (ceci) (au lieu de marquer la méthode comme synchronisée) dans tout le contenu d'une méthode parce qu'elles pensent qu'il est "plus clair pour le lecteur" sur quel objet est réellement synchronisé. Tant que les gens font un choix éclairé (par exemple, comprenez qu'en faisant cela, ils insèrent en fait des bytecodes supplémentaires dans la méthode et cela pourrait avoir un effet d'entraînement sur les optimisations potentielles), je ne vois pas particulièrement de problème avec cela . Vous devez toujours documenter le comportement simultané de votre programme, donc je ne vois pas l'argument "'synchronisé' publie le comportement" comme étant si convaincant.
En ce qui concerne la question de quel verrou d'objet vous devez utiliser, je pense qu'il n'y a rien de mal à synchroniser sur l'objet actuel si cela est attendu par la logique de ce que vous faites et comment votre classe serait généralement utilisée . Par exemple, avec une collection, l'objet que vous vous attendez logiquement à verrouiller est généralement la collection elle-même.
la source
Je pense qu'il y a une bonne explication sur la raison pour laquelle chacune de ces techniques est vitale à votre actif dans un livre intitulé Java Concurrency In Practice de Brian Goetz. Il fait une remarque très claire - vous devez utiliser le même verrou "PARTOUT" pour protéger l'état de votre objet. La méthode synchronisée et la synchronisation sur un objet vont souvent de pair. Par exemple, Vector synchronise toutes ses méthodes. Si vous avez une poignée sur un objet vectoriel et que vous allez faire "mettre si absent", alors simplement la synchronisation vectorielle de ses propres méthodes individuelles ne vous protégera pas de la corruption d'état. Vous devez synchroniser en utilisant synchronized (vectorHandle). Cela entraînera l'acquisition du même verrou par chaque thread qui possède une poignée pour le vecteur et protégera l'état général du vecteur. C'est ce qu'on appelle le verrouillage côté client. Nous savons en fait que le vecteur synchronise (ceci) / synchronise toutes ses méthodes et donc la synchronisation sur l'objet vectorHandle entraînera une synchronisation correcte de l'état des objets vectoriels. Il est insensé de croire que vous êtes thread-safe uniquement 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 de telles opérations atomiques.
En résumé
la source
Non, tu ne devrais pas toujours . Cependant, j'ai tendance à l'éviter lorsqu'il y a plusieurs préoccupations sur un objet particulier qui n'ont besoin que d'être threadsafe en ce qui les concerne. Par exemple, vous pourriez avoir un objet de données mutable qui a des champs "label" et "parent"; ceux-ci doivent être threadsafe, mais changer l'un ne doit pas empêcher l'autre d'être écrit / lu. (En pratique, j'éviterais cela en déclarant les champs volatils et / ou en utilisant les wrappers AtomicFoo de java.util.concurrent).
La synchronisation en général est un peu maladroite, car elle claque un gros verrou plutôt que de penser exactement comment les threads pourraient être autorisés à fonctionner les uns avec les autres. L'utilisation
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 faire cela?Je préférerais de beaucoup avoir des serrures plus granulaires; même si vous voulez tout empêcher de changer (peut-être que vous sérialisez l'objet), vous pouvez simplement acquérir tous les verrous pour obtenir la même chose, et c'est plus explicite de cette façon. Lorsque vous utilisez
synchronized(this)
, vous ne savez pas exactement pourquoi vous effectuez la synchronisation ou quels pourraient être les effets secondaires. Si vous utilisezsynchronized(labelMonitor)
, ou mieux encorelabelLock.getWriteLock().lock()
, il est clair ce que vous faites et à quoi se limitent les effets de votre section critique.la source
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ère éviter de synchroniser (ceci) pour réduire les conflits, mais les verrous privés ajoutent de la complexité dont vous devez être conscient. 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 verrouillage d'instance et lire ce sujet. (Cela dit: l'utilisation de synchronize (this) ne rend pas automatiquement votre classe entièrement thread-safe.) Ce n'est pas un sujet facile, mais une fois que vous vous y êtes habitué, la question de savoir si synchronize (this) ou non vient naturellement .
la source
Un verrou est utilisé pour la visibilité ou pour protéger certaines données contre les modifications simultanées qui peuvent conduire à la course.
Lorsque vous avez juste besoin de faire des opérations de type primitif pour qu'elles soient atomiques, il existe des options disponibles comme
AtomicInteger
et similaires.Mais supposons que vous ayez deux entiers qui sont liés les uns aux autres comme
x
et lesy
coordonnées, qui sont liées les unes aux autres et doivent être modifiées de manière atomique. Ensuite, vous les protégeriez à l'aide d'un même verrou.Un verrou ne doit protéger que l'état qui est lié les uns aux autres. Ni moins ni 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 un état non lié.Dans l'exemple ci-dessus, je n'ai qu'une seule méthode qui mute à la fois
x
ety
pas deux méthodes différentes en tant quex
ety
sont liées et si j'avais donné deux méthodes différentes pour muterx
ety
séparément, cela n'aurait pas été thread-safe.Cet exemple est juste pour démontrer et pas nécessairement la façon dont il doit être mis en œuvre. La meilleure façon de le faire serait de le rendre IMMUTABLE .
Maintenant en opposition à l'
Point
exemple, il y a un exemple deTwoCounters
déjà fourni par @Andreas où l'état qui est protégé par deux verrous différents car l'état n'est pas lié l'un à l'autre.Le processus d'utilisation de différents verrous pour protéger des états indépendants s'appelle Lock Striping ou Lock Splitting
la source
La raison de ne pas synchroniser sur ce point est que parfois vous avez besoin de plusieurs verrous (le deuxième verrou est souvent supprimé après une réflexion supplémentaire, mais vous en avez toujours besoin à l'état intermédiaire). Si vous verrouillez ceci , vous devez toujours vous rappeler lequel des deux verrous est présent ; si vous verrouillez un objet privé, le nom de la variable vous le dit.
Du point de vue du lecteur, si vous voyez le verrouillage sur cela , vous devez toujours répondre aux deux questions:
Un exemple:
Si deux threads commencent
longOperation()
sur deux instances différentes deBadObject
, ils acquièrent leurs verrous; quand il est temps d'appelerl.onMyEvent(...)
, nous avons un blocage car aucun des threads ne peut acquérir le verrou de l'autre objet.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.
la source
BadObject
A invoquelongOperation
sur B, en passant AmyListener
, et vice versa. Pas impossible, mais assez compliqué, soutenant mes points précédents.Comme déjà dit ici, le bloc synchronisé peut utiliser une variable définie par l'utilisateur comme objet de verrouillage, lorsque la fonction synchronisée utilise uniquement "ceci". Et bien sûr, vous pouvez manipuler des 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 "ceci" comme objet de verrouillage. Ce n'est pas vrai, la différence est dans le code d'octet qui sera généré dans les deux situations. En cas d'utilisation de bloc synchronisée, une variable locale doit être allouée qui contient une référence à "ceci". Et en conséquence, nous aurons une taille de fonction un peu plus grande (non 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
De plus, l'utilisation du bloc synchronisé n'est pas bonne en raison du point de vue suivant:
Pour plus de détails dans ce domaine, je vous recommande de lire cet article: http://java.dzone.com/articles/synchronized-considered
la source
C'est vraiment juste un complément aux autres réponses, mais si votre principale objection à l'utilisation d'objets privés pour le verrouillage est qu'elle encombre votre classe avec des champs qui ne sont pas liés à la logique métier, alors Project Lombok doit
@Synchronized
générer le passe-partout au moment de la compilation:compile en
la source
Un bon exemple d'utilisation synchronisée (ceci).
Comme vous pouvez le voir ici, nous utilisons synchronize sur cela pour coopérer facilement de manière longue (éventuellement boucle infinie de la méthode d'exécution) avec certaines méthodes synchronisées.
Bien sûr, il peut être très facilement réécrit en utilisant synchronisé sur un champ privé. Mais parfois, lorsque nous avons déjà une conception avec des méthodes synchronisées (c.-à-d. Classe héritée, nous dérivons de, synchronisé (ceci) peut être la seule solution).
la source
this
. Ce pourrait être un domaine privé.Cela dépend de la tâche que vous voulez faire, mais je ne l'utiliserais pas. De plus, vérifiez si la sauvegarde du thread que vous souhaitez accompagner ne peut pas être effectuée en synchronisant (ceci) en premier lieu? Il y a aussi de beaux verrous dans l'API qui pourraient vous aider :)
la source
Je veux seulement mentionner une solution possible pour des références privées uniques dans des parties atomiques de code sans dépendances. Vous pouvez utiliser un Hashmap statique avec des verrous et une méthode statique simple nommée atomic () qui crée automatiquement les références requises en utilisant les informations de la pile (nom complet de la classe et numéro de ligne). Vous pouvez ensuite utiliser cette méthode pour synchroniser des instructions sans écrire de nouvel objet de verrouillage.
la source
Évitez d'utiliser
synchronized(this)
comme mécanisme de verrouillage: cela verrouille l'instance de classe entière et peut provoquer des blocages. Dans de tels cas, refactorisez le code pour verrouiller uniquement une méthode ou une variable spécifique, de cette façon, toute la classe ne sera pas verrouillée.Synchronised
peut être utilisé au niveau de la méthode.Au lieu d'utiliser
synchronized(this)
, le code ci-dessous montre comment vous pouvez simplement verrouiller une méthode.la source
Mes deux cents en 2019 même si cette question aurait déjà pu être réglée.
Le verrouillage sur «ceci» n'est pas mauvais si vous savez ce que vous faites mais derrière la scène, le verrouillage sur «ceci» est (ce que permet malheureusement le mot-clé synchronisé dans la définition de méthode).
Si vous voulez que les utilisateurs de votre classe puissent «voler» votre verrou (c'est-à-dire empêcher d'autres threads de le gérer), vous voulez en fait que toutes les méthodes synchronisées attendent pendant qu'une autre méthode de synchronisation s'exécute, etc. Il doit être intentionnel et bien pensé (et donc documenté pour aider vos utilisateurs à le comprendre).
Pour développer davantage, à l'inverse, vous devez savoir ce que vous `` gagnez '' (ou `` perdez '') si vous verrouillez un verrou non accessible (personne ne peut `` voler '' votre verrou, vous avez le contrôle total, etc.). ..).
Le problème pour moi est que le mot-clé synchronisé dans la signature de définition de méthode rend trop facile pour les programmeurs de ne pas penser à quoi verrouiller, ce qui est une chose très importante à penser si vous ne voulez pas rencontrer de problèmes dans un multi -programme fileté.
On ne peut pas affirmer que «généralement» vous ne voulez pas que les utilisateurs de votre classe puissent faire ces choses ou que «généralement» vous voulez ... Cela dépend de la fonctionnalité que vous codez. Vous ne pouvez pas faire une règle empirique car vous ne pouvez pas prédire tous les cas d'utilisation.
Considérez par exemple le rédacteur qui utilise un verrou interne mais les gens ont du mal à l'utiliser à partir de plusieurs threads s'ils ne veulent pas que leur sortie s'entrelace.
Si votre serrure est accessible en dehors de la classe ou non, c'est votre décision en tant que programmeur en fonction des fonctionnalités de la classe. Il fait partie de l'API. Vous ne pouvez pas passer par exemple de synchronisé (ceci) à synchronisé (provateObjet) sans risquer de casser les changements dans le code en l'utilisant.
Remarque 1: je sais que vous pouvez réaliser tout ce qui est synchronisé (ceci) en utilisant un objet de verrouillage explicite et en l'exposant, mais je pense que ce n'est pas nécessaire si votre comportement est bien documenté et que vous savez réellement ce que le verrouillage sur `` cela '' signifie.
Note 2: Je ne suis pas d'accord avec l'argument selon lequel si du code vole accidentellement votre verrou, c'est un bug et vous devez le résoudre. C'est en quelque sorte le même argument que de dire que je peux rendre toutes mes méthodes publiques même si elles ne sont pas censées être publiques. Si quelqu'un appelle «accidentellement» ma méthode destinée à être privée, c'est un bug. Pourquoi activer cet accident en premier lieu !!! Si la capacité de voler votre serrure est un problème pour votre classe, ne le permettez pas. Aussi simple que cela.
la source
Je pense que les points un (quelqu'un d'autre utilisant votre verrou) et deux (toutes les méthodes utilisant inutilement le même verrou) peuvent se produire dans n'importe quelle application assez grande. Surtout quand il n'y a pas de bonne communication entre les développeurs.
Ce n'est pas coulé dans le béton, c'est surtout une question de bonnes pratiques et de prévention des erreurs.
la source