Éviter de synchroniser (ceci) en Java?
chaque fois qu'une question surgit sur SO à propos de la synchronisation Java, certaines personnes sont très impatientes de souligner que synchronized(this)
devrait être évitée. Au lieu de cela, ils prétendent qu'il est préférable de verrouiller une référence privée.
certaines des raisons données sont:
- certains mauvais code peut voler votre serrure (très populaire cet, a aussi un "accidentellement" variante)
- toutes les méthodes synchronisées dans la même classe utilisent la même serrure, ce qui réduit le débit
- vous êtes (inutilement) de l'exposer trop d'informations
D'autres personnes, dont moi, soutiennent que synchronized(this)
est un idiome qui est beaucoup utilisé (également dans les bibliothèques Java), est sûr et bien compris. Il ne devrait pas être évité parce que 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: si elle est applicable, utilisez-la.
je suis intéressé de voir quelques exemples du monde réel (pas de truc de foobar) où éviter un verrouillage sur this
est préférable quand synchronized(this)
ferait aussi le travail.
donc: devriez-vous toujours éviter synchronized(this)
et le remplacer par une serrure sur une référence privée?
quelques informations complémentaires (mises à jour au fur et à mesure que les réponses sont données):
- nous parlons de synchronisation d'instance
- les méthodes tant implicites (
synchronized
) qu'explicites desynchronized(this)
sont considérées comme - si vous citez Bloch ou d'autres autorités sur le sujet, n'oubliez pas les parties que vous n'aimez pas (par exemple Java efficace, point sur la sécurité du Thread: généralement, c'est le verrou sur l'instance lui-même, mais il y a des exceptions.)
- si vous avez besoin de granularité dans votre verrouillage autre que
synchronized(this)
, alorssynchronized(this)
n'est pas applicable, donc ce n'est pas le problème
18 réponses
je vais couvrir chaque point séparément.
-
certains Code mal peuvent voler votre serrure (très populaire celui-ci, a également un variante" accidentellement")
je suis plus inquiet au sujet de accidentellement . Ce qui revient à dire que cette utilisation de
this
fait partie de l'interface exposée de votre classe, et devrait être documentée. Parfois la capacité d'un autre code d'utiliser votre serrure est souhaitée. Cela est vrai pour des choses commeCollections.synchronizedMap
(voir le javadoc). -
toutes les méthodes synchronisées dans la même classe utilisent le même lock, 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 adéquate du débit demandera plus de réflexion. -
Vous êtes (inutilement) de l'exposer trop d'informations
C'est une variante de #1. L'utilisation de
synchronized(this)
fait partie de votre interface. Si vous ne voulez pas/besoin de cet exposé, ne pas le faire.
Eh bien, tout d'abord, il convient de souligner que:
public void blah() {
synchronized (this) {
// do stuff
}
}
est sémantiquement équivalent à:
public synchronized void blah() {
// do stuff
}
qui est une raison de ne pas utiliser synchronized(this)
. Vous pourriez argumenter que vous pouvez faire des choses autour du bloc synchronized(this)
. La raison habituelle est d'essayer et d'éviter d'avoir à faire le contrôle synchronisé du tout, ce qui conduit à toutes sortes de problèmes de concurrence, en particulier le double vérifié-problème de verrouillage qui va juste pour montrer combien il peut être difficile de faire un relativement simple vérification des threads.
une serrure privée est un mécanisme de défense, ce qui n'est jamais une mauvaise idée.
aussi, comme vous l'avez mentionné, les écluses privées peuvent contrôler la granularité. Un ensemble d'opérations sur un objet peut être totalement étranger à l'autre, mais synchronized(this)
va s'excluent l'accès à tous.
synchronized(this)
ne vous donne vraiment rien.
pendant que vous utilisez synchronized(ceci) vous utilisez l'instance de classe comme une serrure elle-même. Cela signifie que pendant que la serrure est acquise par le fil 1 Le fil 2 doit attendre
supposons le code suivant
public void method1() {
do something ...
synchronized(this) {
a ++;
}
................
}
public void method2() {
do something ...
synchronized(this) {
b ++;
}
................
}
Méthode 1 modifier la variable a et la méthode 2 Modifier la variable b , la modification simultanée de la même variable par deux fils doit être évitée et il est. MAIS alors que thread1 modifier un et thread2 modifier b elle peut être réalisée sans aucune condition de course.
malheureusement, le code ci-dessus ne permettra pas cela puisque nous utilisons la même référence pour une serrure; cela signifie que les threads même s'ils ne sont pas dans une condition de course devraient attendre et évidemment le code sacrifie la concurrence du programme.
le la solution est d'utiliser 2 serrures différentes 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 des serrures à grain plus fin (2 serrures au lieu d'une ( lockA et lockB pour les variables a et b respectivement) et, par conséquent, permet une meilleure concurrence, d'autre part, il est devenu 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 de serrure" vous semble-t-il si excentrique? Un thread pourrait en effet acquérir le verrouillage sur votre objet "extérieurement" ( synchronized(theObject) {...}
), bloquant d'autres threads attendant les méthodes d'instance synchronisées.
si vous ne croyez pas au code malveillant, considérez que ce code pourrait provenir de tiers (par exemple si vous développez une sorte de serveur d'application).
le la version " accidentelle "semble moins probable, mais comme ils disent,"faire quelque chose idiot-preuve et quelqu'un inventera un meilleur idiot".
donc je suis d'accord avec l'école de pensée it-depends-on-the-class-does.
Modifier les 3 premiers commentaires d'eljenso:
Je n'ai jamais connu le problème de vol de serrure, mais voici un scénario imaginaire:
Disons que votre système est un conteneur servlet, et l'objet que nous considérons est l'implémentation ServletContext
. Sa méthode getAttribute
doit être thread-safe, car les attributs de contexte sont des données partagées; vous la déclarez donc comme synchronized
. Imaginons aussi que vous fournissiez un service d'hébergement public basé sur l'implémentation de votre conteneur.
je suis votre client et déployer mon" bon " servlet sur votre site. Il se trouve que mon code contient un appel à getAttribute
.
un hacker, déguisé en un autre client, déploie son servlet malveillant sur votre site. Il contient le code suivant dans la méthode init
:
synchronized (this.getServletConfig().getServletContext()) { while (true) {} }
en supposant que nous partagions le même contexte de servlet (autorisé par le spec tant que les deux servlets sont sur le même hôte virtuel), mon appel sur getAttribute
est verrouillé pour toujours. Le hacker a obtenu un DoS sur mon servlet.
Cette attaque n'est pas possible si getAttribute
est synchronisé sur une serrure privée, parce que le code tiers ne peut pas acquérir cette serrure.
je reconnais que l'exemple est artificiel et une vision trop simpliste de la façon dont un conteneur servlet fonctionne, mais IMHO il prouve le point.
donc je ferais mon choix de conception basé sur des considérations de sécurité: aurai-je le contrôle complet sur le code qui a accès aux instances? Quelle serait la conséquence d'un thread est un verrou sur une instance indéfiniment?
il semble y avoir un consensus différent dans les camps de C# et de 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 sans doute plus sûr:
// 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
}
l'idiome C# est certainement plus sûr. Comme mentionné précédemment, aucun accès malveillant / accidentel à la serrure ne peut être fait de l'extérieur de l'instance. Le code Java comporte aussi ce risque, mais il semble que la communauté Java a gravité au fil du temps vers la version légèrement moins sûre, mais légèrement plus courte.
ce n'est pas une attaque contre Java, juste un reflet de mon expérience de travail sur les deux langues.
cela dépend de la situation.
S'il n'y a qu'une seule entité de partage ou plus d'une.
Voir exemple complet ici
une petite introduction.
Threads et partageable entités
Il est possible pour plusieurs threads d'accéder à la même entité, par exemple plusieurs connectionThreads partage une seule correspondance. Depuis que les threads tournent en même temps, il peut y avoir une chance d'écraser les données de l'un par une autre, ce qui peut être une situation perturbée.
Nous devons donc trouver un moyen de nous assurer que l'entité partageable n'est accessible que par un seul fil à la fois. (SIMULTANÉITÉ.)
bloc synchronisé
synchronisée() bloc est un moyen de garantir l'accès simultané de partageables entité.
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 toilette 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 que p1 termine son travail
P1 déverrouille la porte
alors seulement p1 peut utiliser lavabo.
synchronized(this)
{
SHARED_ENTITY.....
}
"cette" condition intrinsèque de verrouillage associés à la classe (développeur Java conçu Objet de la classe de telle sorte que chaque objet peut travailler comme moniteur).
L'approche ci-dessus fonctionne bien lorsqu'il n'y a qu'une seule entité partagée et plusieurs threads (1: N).
n entités partageables-m threads
Maintenant, pensez à une situation où il y a deux lavabos à l'intérieur d'une toilette et une seule porte. Si nous utilisons l'approche précédente, seul le p1 peut utiliser un lavabo à la fois alors que le p2 attend à l'extérieur. C'est un gaspillage de ressources car personne n'utilise B2 (washbasin).
Une approche plus sage serait de créer une salle plus petite à l'intérieur de la toilette 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.
washbasin1;
washbasin2;
Object lock1=new Object();
Object lock2=new Object();
synchronized(lock1)
{
washbasin1;
}
synchronized(lock2)
{
washbasin2;
}
voir plus sur les fils ----> ici
le paquet java.util.concurrent
a considérablement réduit la complexité de mon code de sécurité. Je n'ai que des preuves anecdotiques pour continuer, mais la plupart des travaux que j'ai vus avec synchronized(x)
semblent être ré-implémenter une serrure, un sémaphore, ou un loquet, mais en utilisant les moniteurs de niveau inférieur.
dans cette optique, la synchronisation à l'aide de l'un de ces mécanismes est analogue à la synchronisation sur un objet interne, plutôt que la fuite d'une serrure. Cela est bénéfique en ce que vous avez absolu la certitude que vous contrôlez l'entrée dans le moniteur par deux ou plusieurs threads.
si vous avez décidé que:
- la chose que vous devez faire est de serrure sur l'objet courant; et
- vous voulez verrouiller avec granularité plus petit que une méthode entière;
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 synchronisée) à l'intérieur du contenu entier d'une méthode parce qu'ils pensez qu'il est "plus clair pour le lecteur" quel objet est réellement synchronisé sur. Tant que les gens font un choix éclairé (par exemple, comprendre qu'en le faisant ils insèrent des codes byt supplémentaires dans la méthode et cela pourrait avoir un effet d'entraînement sur les optimisations potentielles), Je ne vois pas de problème particulier avec cela. Vous devriez toujours documenter le comportement concurrent de votre programme, de sorte que je ne vois pas l'argument "synchronisé" publie le comportement comme étant si convaincant.
en ce qui concerne la question de la serrure de l'objet que vous devez utiliser, je pense qu'il n'y a rien de mal à synchroniser sur l'objet actuel si cela est prévu par la logique de ce que vous faites et comment votre classe serait typiquement utilisé . Par exemple, avec une collection, l'objet que vous vous attendez logiquement à verrouiller est généralement la collection elle-même.
- faites vos données immuables si c'est possible (
final
variables) - si vous ne pouvez pas éviter la mutation de données partagées à travers plusieurs threads, utilisez des constructions de programmation de haut niveau [par exemple, l'API
Lock
granulaire ]
une serrure fournit un accès exclusif à une ressource partagée: un seul thread à la fois peut acquérir la serrure et tout accès à la ressource partagée exige que la serrure soit acquis en premier.
exemple de code à utiliser ReentrantLock
qui implémente Lock
interface
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 L'écluse sur synchronisée (ceci)
-
l'utilisation de méthodes ou d'énoncés synchronisés oblige l'acquisition et la libération de toutes les écluses d'une manière structurée par blocs.
-
serrure les implémentations fournissent une fonctionnalité supplémentaire par rapport à l'utilisation de méthodes et d'instructions synchronisées en fournissant
- Un non-tentative de blocage pour acquérir un verrou (
tryLock()
) - tentative d'acquisition de la serrure pouvant être interrompue (
lockInterruptibly()
) - tentative d'acquisition de la serrure qui peut temporiser (
tryLock(long, TimeUnit)
).
- Un non-tentative de blocage pour acquérir un verrou (
-
une classe de serrure peut également fournir le comportement et la sémantique qui est tout à fait différent de celui de la serrure de moniteur implicite, tels que
- commande garantie
- non-re participant utilisation
- détection de blocage
Regardez cette question concernant divers types de Locks
:
synchronisation vs verrouillage
vous pouvez obtenir la sécurité du thread en utilisant l'API de concurrence avancée au lieu de blocs synchronisés. Cette documentation page fournit de bonnes constructions de programmation pour atteindre la sécurité de filetage.
les objets de verrouillage supportent des idiomes de verrouillage qui simplifient de nombreuses applications concurrentes.
exécuteurs testamentaires définit une API de haut niveau pour le lancement et la gestion des threads. Implémentations exécuteurs fournies par java.util.gestion simultanée du pool de threads approprié pour les applications à grande échelle.
Concurrent Collections rendre plus facile à gérer de grands ensembles de données, et peut réduire considérablement la nécessité pour la synchronisation.
Atomique Les Variables ont des caractéristiques qui minimisent la synchronisation et aident à éviter les erreurs de cohérence de la mémoire.
au Royaume-Uni (dans JDK 7) fournit la génération efficace de nombres de pseudorandom à partir de fils multiples.
Consultez de java.util.concurrent et java.util.simultané.atomique aussi les paquets pour les autres constructions de programmation.
je pense qu'il y a une bonne explication sur la raison pour laquelle chacune de ces techniques sont vitales sous votre ceinture dans un livre appelé Java Concourency In Practice par Brian Goetz. Il fait un point très clair - vous devez utiliser la même serrure "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: Le vecteur synchronise toutes ses méthodes. Si vous avez une poignée pour un objet vectoriel et allons le faire "mettre si absent" alors simplement Vecteur synchroniser ses propres méthodes ne va pas vous protéger de la corruption de l'état. Vous devez synchroniser en utilisant synchronisé (vectorHandle). Il en résultera que le même verrou sera acquis par chaque fil qui a une poignée vers 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 effet que vector synchronise (ceci) / synchronise toutes ses méthodes et donc que la synchronisation sur le vectorHandle de l'objet se traduira par synchronisation correcte de l'état des objets vectoriels. Il est insensé de croire que vous êtes thread safe juste parce que vous utilisez une collection de thread safe. C'est précisément la raison pour laquelle Competienthashmap a explicitement introduit la méthode putIfAbsent - pour rendre de telles opérations atomiques.
en résumé
- la synchronisation au niveau de la méthode permet le verrouillage côté client.
- si vous avez un objet de verrouillage privé - il rend le verrouillage côté client impossible. C'est très bien si vous savez que votre classe n'a pas "mis si absent" type de fonctionnalité.
- si vous concevez une bibliothèque - alors synchroniser sur ce ou synchroniser la méthode est souvent plus sage. Parce que vous êtes rarement en position de décider comment votre classe va être utilisé.
- si Vector avait utilisé un objet de verrouillage privé - il aurait été impossible d'obtenir" put if absent " droite. Le code client ne sera jamais obtenir une poignée à la verrouillage privé violant ainsi la règle fondamentale d'utiliser la même serrure pour protéger son état.
- synchroniser sur ce ou méthodes synchronisées ont un problème comme d'autres l'ont fait remarquer - quelqu'un pourrait obtenir une serrure et ne jamais la libérer. Tous les autres fils continueraient à attendre que la serrure soit libérée.
- alors sachez ce que vous faites et adoptez celle qui est correcte.
- Quelqu'un a fait valoir qu'avoir un objet de verrouillage privé donne vous avez une meilleure granularité-par exemple si deux opérations ne sont pas liées - ils pourraient être gardés par des serrures différentes résultant en un meilleur débit. Mais je pense que c'est l'odeur du design et pas l'odeur du code - si deux opérations sont complètement sans rapport pourquoi font-elles partie de la même classe? Pourquoi une classe club indépendants de fonctionnalités? Peut-être une classe utilitaire? Hmmmmm-certains util fournissant la manipulation de chaîne et le formatage de date de calendrier à travers la même instance?? ... n'a aucun sens pour moi moins!!
Non, vous ne devriez pas toujours . Cependant, j'ai tendance à l'éviter quand il ya des préoccupations multiples sur un objet particulier qui n'ont besoin d'être threadsafe en ce qui concerne eux-mêmes. Par exemple, vous pouvez 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 volatiles et/ou en utilisant java.util.simultané Atomicfoo wrappers).
la synchronisation en général est un peu maladroite, car elle ferme un grand verrou plutôt que de penser exactement comment les fils pourraient être autorisés à travailler les uns autour des autres. Utiliser synchronized(this)
est encore plus maladroit et antisocial, car il dit "Personne ne peut changer n'importe quoi sur cette classe pendant que je tiens la serrure". Combien de fois dois-tu vraiment faire ça?
je préférerais avoir des serrures plus granulaires; même si vous faites vous voulez arrêter tout de changer (peut-être que vous sérialisez l'objet), vous pouvez juste acquérir toutes les serrures pour atteindre la même chose, plus il est plus explicite de cette façon. Lorsque vous utilisez synchronized(this)
, il n'est pas clair exactement pourquoi vous êtes synchroniser, ou ce que les effets secondaires pourraient être. Si vous utilisez synchronized(labelMonitor)
, ou encore mieux labelLock.getWriteLock().lock()
, il est clair ce que vous faites et ce que les effets de votre section critique sont limités à.
brève réponse : vous devez comprendre la différence et faire un choix en fonction du code.
longue réponse : en général, je préfère essayer d'éviter synchroniser(ce) pour réduire la controverse, mais les serrures privées ajoutent la complexité que vous devez être conscient. Utilisez donc la bonne synchronisation pour le bon travail. Si vous n'avez pas autant d'expérience avec la programmation multi-threads, je préfère m'en tenir à verrouillage d'instance et lecture sur ce sujet. (Cela dit: juste utiliser synchroniser (ceci) 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 réponse à savoir s'il faut utiliser synchroniser(ceci) ou non vient naturellement.
une serrure est utilisée pour visibilité ou pour protéger certaines données de modification simultanée qui peut conduire à la course.
quand vous avez besoin de juste faire des opérations de type primitif pour être atomique il y a des options disponibles comme AtomicInteger
et les goûts.
mais supposons que vous ayez deux entiers qui sont liés l'un à l'autre comme x
et y
coordonnées, qui sont liées les unes aux autres et doivent être modifiées de manière atomique. Vous les protégeriez avec la même serrure.
une serrure ne doit protéger que l'état qui est apparenté. Rien de moins et rien de plus. Si vous utilisez synchronized(this)
dans chaque méthode, alors même si l'état de la classe n'est pas lié, tous les threads seront confrontés à une contestation, même si vous mettez à jour l'état non lié.
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 les deux x
et y
, et non de deux méthodes différentes comme x
et y
sont liées et si j'avais donné deux méthodes différentes pour la mutation x
et y
séparément, puis il n'aurait pas été thread-safe.
cet exemple ne sert qu'à 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 faire immuable .
maintenant en opposition à Point
exemple, il y a un exemple de TwoCounters
déjà fourni par @Andreas où l'état qui est protégé par deux serrures différentes que l'état est sans rapport les uns avec les autres.
le processus d'utilisation de différentes serrures pour protéger des États indépendants est appelé bande de serrure ou fendage de serrure
la raison de ne pas synchroniser sur ce est que parfois vous avez besoin de plus d'une serrure (la deuxième serrure est souvent retirée après quelques réflexions supplémentaires, mais vous en avez encore besoin dans l'état intermédiaire). Si vous verrouillez sur ce , vous devez toujours vous rappeler lequel des deux verrous est ce ; si vous verrouillez sur un objet privé, le nom de la variable vous dit cela.
du livre du lecteur point de vue, si vous voyez le verrouillage sur ce , vous devez toujours répondre aux deux questions:
- quel type d'accès est protégé par ce ?
- est-ce qu'une serrure suffit vraiment, 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 serrures; quand il est temps d'invoquer l.onMyEvent(...)
, nous avons une impasse parce qu'aucun des fils peuvent acquérir la serrure de l'autre objet.
dans cet exemple, nous pouvons éliminer l'impasse en utilisant deux serrures, l'une pour les opérations courtes et l'autre pour les opérations longues.
comme déjà dit ici, le bloc synchronisé peut utiliser la variable définie par l'utilisateur comme objet lock, lorsque la fonction synchronisée n'utilise que"ceci". Et bien sûr, vous pouvez manipuler avec des zones de votre fonction qui devrait être synchronisée et ainsi de suite.
mais tout le monde dit qu'aucune différence entre fonction synchronisée et 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 octet qui sera généré dans les deux situations. Dans en cas d'utilisation de blocs synchronisés, il convient d'attribuer une variable locale contenant une référence à "ceci". Et comme résultat nous aurons un peu plus grande taille de la fonction (pas pertinent si vous avez seulement peu de nombre de fonctions).
explication plus détaillée de la différence vous pouvez trouver ici: http://www.artima.com/insidejvm/ed2/threadsynchP.html
aussi l'utilisation de bloc synchronisé n'est pas bon en raison du point de vue suivant:
le mot-clé synchronisé est très limité dans un domaine: lors de la sortie d'un bloc synchronisé, tous les threads qui attendent cette serrure doivent être débloqués, mais un seul de ces threads peut prendre la serrure; tous les autres voient que la serrure est prise et retourner à l'état bloqué. Ce n'est pas seulement un grand nombre de cycles de traitement gaspillés: souvent le commutateur de contexte pour débloquer un thread implique également la mémoire de paging hors du disque, et c'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(ceci).
// 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...
}
comme vous pouvez le voir ici, nous utilisons synchroniser sur ce pour coopérer facilement de méthode lengthly (éventuellement boucle infinie de la méthode run) avec certaines méthodes synchronisées.
bien sûr, il peut être très facilement réécrit en utilisant synchronisé sur le domaine privé. Mais parfois, quand nous avons déjà une certaine conception avec des méthodes synchronisées (i.e. classe legacy, nous dérivons de, synchronisé(ceci) peut la seule solution).
cela dépend de la tâche que vous voulez accomplir, mais je ne l'utiliserais pas. Aussi, vérifiez si le thread-save-ness que vous voulez accompagner ne pourrait pas être fait en synchronisant(ceci) en premier lieu? Il y a aussi de jolies serrures dans L'API qui pourraient vous aider:)
je pense que les points un (quelqu'un d'autre utilisant votre serrure) et deux (toutes les méthodes utilisant la même serrure inutilement) 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 du béton, c'est surtout une question de bonnes pratiques et de prévention des erreurs.