Éviter synchronisé (ceci) en Java?


Chaque fois qu'une question apparaît sur SO à propos de la synchronisation Java, certaines personnes sont très impatientes de souligner que synchronized(this) doit ê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:

D'autres personnes, y compris moi, soutiennent que synchronized(this) est un idiome qui est beaucoup utilisé (également dans les bibliothèques Java), est sûr et bien compris. Cela ne devrait pas être évité car vous avez un bug et vous n'avez aucune 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 quelques exemples du monde réel (pas de foobar stuff) où éviter un verrou sur this est préférable lorsque synchronized(this) ferait également le travail.

Donc: devriez-vous toujours é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 que les réponses sont données):

  • nous parlons de synchronisation d'instance
  • les méthodes implicites (synchronized) et la forme explicite de synchronized(this) sont considérées comme
  • si vous citez Bloch ou d'autres autorités sur le sujet, ne laissez de côté les parties que vous n'aimez pas (par exemple Java efficace, élément sur la sécurité des threads: Typiquement, c'est le verrou sur l'instance elle-même, mais il y a des exceptions.)
  • si vous avez besoin de granularité dans votre verrouillage autre que synchronized(this) fournit, alors synchronized(this) n'est pas applicable, donc ce n'est pas le problème
Author: Community, 2009-01-14

18 answers

Je couvrirai chaque point séparément.

  1. Un code maléfique peut voler votre serrure (très populaire celui-ci, a également un variante "accidentellement")

    Je suis plus inquiet pour accidentellement. 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 de choses comme Collections.synchronizedMap (voir le javadoc).

  2. Toutes les méthodes synchronisées dans la même classe utilisent exactement la même chose verrouiller, 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 bonne synchronisation pour le débit prendra plus de réflexion.

  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 exposé, ne faites pas il.

 116
Author: Darron, 2016-04-19 18:40:08

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
}

Ce qui est une des raisons de 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 d'éviter d'avoir à faire la vérification synchronisée, ce qui conduit à toutes sortes de problèmes de concurrence, en particulier le double checked-locking problem, ce qui montre à quel point il peut être difficile de faire une vérification relativement simple des threads.

Un verrou privé 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) ça ne te donne rien.

 83
Author: cletus, 2009-01-14 12:51:15

Pendant que vous utilisez synchronized(this), vous utilisez l'instance de classe comme un verrou lui-même. Cela signifie que pendant que lock 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 ++;      
    }
    ................
}

Méthode 1 modification de la variable un et la méthode 2 modification de la variable b, les modifications simultanées de la même variable par deux fils doivent être évités et il est. MAIS alors que thread1 modification de un et thread2 modifier b il peut être effectué sans aucune 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, devraient attendre et évidemment le code sacrifie la concurrence du programme.

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 verrous plus fins (2 verrous au lieu d'un (lockA et lockB pour les variables un et b, respectivement), et comme un résultat permettant de mieux simultanéité, d'autre part, il est devenu plus complexe que le premier exemple ...

 50
Author: Andreas Bakurov, 2011-03-03 01:36:50

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 verrou sur votre objet "en externe" (synchronized(theObject) {...}), bloquant les autres threads en attente sur 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'applications).

La version" accidentelle " semble moins probable, mais comme ils disent: "faites quelque chose à l'épreuve des idiots et quelqu'un inventera un meilleur idiot".

Je suis donc d'accord avec l'école de pensée qui dépend de ce que fait la classe.


Modifier en suivant 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 de 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 les 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 je déploie mon "bon" servlet sur votre site. Il arrive que mon code contienne un appel à getAttribute.

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:

synchronized (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 getAttribute est verrouillé pour toujours. Le pirate a réalisé un DoS sur mon servlet.

Cette attaque n'est pas possible si getAttribute 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 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 basé sur la sécurité considération: aurai-je un contrôle complet sur le code qui a accès aux instances? Quelle serait la conséquence d'un thread tenant indéfiniment un verrou sur une instance?

 14
Author: Olivier, 2009-01-14 14:42:30

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:

// 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 au verrou ne peut être effectué depuis l'extérieur de l'instance. Le code Java a aussi ce risque, mais il semble que la communauté Java a gravité au fil du temps vers le moins sûr, mais version un peu plus laconique.

Cela ne signifie pas une fouille contre Java, juste un reflet de mon expérience de travail sur les deux langues.

 11
Author: serg10, 2009-01-14 12:43:00

Cela dépend de la situation.
S'il n'y a qu'une seule entité de partage ou plus d'une.

Voir en plein travail exemple ici

Une petite introduction.

Fils et partageable entités
Il est possible pour plusieurs threads d'accéder à la même entité, par exemple plusieurs connectionThreads partageant un seul messageQueue. Étant donné que les threads s'exécutent simultanément il peut y avoir une chance de remplacer ses données par une autre qui peut être un situation foirée.
Nous avons donc besoin d'un moyen de nous assurer que l'entité partageable n'est accessible que par un thread à la fois. (SIMULTANÉITÉ).

Synchronisé bloc
synchronized () block est un moyen d'assurer l'accès simultané d'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 il y a une porte (serrure).
Maintenant, nous voulons qu'une personne utilise le lavabo à la fois.
Une approche est de verrouiller la porte par P1 lorsque la porte est verrouillée P2 attend que p1 termine son travail
P1 ouvre la porte
ensuite, seul p1 peut utiliser le lavabo.

Syntaxe.

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

"this" a fourni le verrou intrinsèque associé à la classe (Java developer a conçu la classe Object de telle sorte que chaque objet puisse fonctionner comme moniteur). L'approche ci-dessus fonctionne bien lorsqu'il n'y a qu'une seule entité partagée et plusieurs threads (1: N).
entrez la description de l'image ici N entités partageables-M fils
Pensez 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 lavabo à la fois tandis que p2 attendra à l'extérieur. 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.

washbasin1;  
washbasin2;

Object lock1=new Object();
Object lock2=new Object();

  synchronized(lock1)
  {
    washbasin1;
  }

  synchronized(lock2)
  {
    washbasin2;
  }

entrez la description de l'image ici
entrez la description de l'image ici

En Voir plus sur les Threads----> ici

 8
Author: Rohit Singh, 2018-10-04 17:33:37

Le paquet java.util.concurrent a considérablement réduit la complexité de mon code thread safe. Je n'ai que des preuves anecdotiques à faire, mais la plupart des travaux que j'ai vus avec synchronized(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 la fuite d'un verrou. Ceci est bénéfique en ce que vous avez la certitude absolue que vous contrôlez l'entrée dans le moniteur par deux threads ou plus.

 7
Author: jamesh, 2009-01-14 14:34:47

Si vous avez décidé que:

  • la chose que vous devez faire est de verrouiller l'objet courant; et
  • vous voulez verrouillez-le 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 synchronized(this) (au lieu de marquer la méthode synchronized) dans tout le contenu d'une méthode car elles pensent qu'il est "plus clair pour le lecteur" sur quel objet est réellement synchronisé. Si tant que les gens font un choix éclairé (par exemple comprendre que ce faisant, 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 "'synchronized' publishes the behaviour" comme étant si convaincant.

Quant à la question de savoir quel verrou d'objet vous devez utiliser, je pense qu'il y a rien de mal à synchroniser sur l'objet courant si cela serait 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 attendez logiquement à verrouiller est généralement la collection elle-même.

 5
Author: Neil Coffey, 2009-01-14 14:59:48
  1. Rendre vos données immuables si c'est possible ( final variables)
  2. Si vous ne pouvez pas éviter la mutation de données partagées sur plusieurs threads, utilisez des constructions de programmation de haut niveau [par exemple granular Lock API]

Un verrou fournit un accès exclusif à une ressource partagée: un seul thread à la fois peut acquérir le verrou et tout accès à la ressource partagée nécessite que le verrou 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 Serrure sur Synchronized(this)

  1. L'utilisation de méthodes ou d'instructions synchronisées force toutes les acquisitions et relâchements de verrouillage à se produire de manière structurée par blocs.

  2. Les implémentations de verrouillage fournissent 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'acquérir le verrou que peut être interrompu (lockInterruptibly())
    3. Une tentative d'acquérir le verrou qui peut expirer (tryLock(long, TimeUnit)).
  3. Une classe de verrouillage peut également fournir un comportement et une sémantique très différents de ceux du verrou de moniteur implicite, tels que

    1. commande garantie
    2. utilisation non récurrente
    3. détection de Blocage

Jetez un oeil à cette question SE concernant divers types de Locks:

Synchronisation vs Serrure

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

Verrouiller les Objets prise en charge des idiomes de verrouillage qui simplifient de nombreuses applications simultanées.

Exécuteurs définissez une API de haut niveau pour le lancement et la gestion des threads. Implémentations d'exécuteurs fournis par Java.util.concurrent fournit une gestion de pool de threads adaptée aux applications à grande échelle.

Collections Simultanées facilitez la gestion de grandes collections de données et réduisez considérablement le besoin de synchronisation.

Variables Atomiques avoir des fonctionnalités qui minimisent la synchronisation et aident à éviter les erreurs de cohérence de la mémoire.

ThreadLocalRandom (dans le JDK 7) permet la génération efficace de pseudorandom nombres de plusieurs threads.

Référez-vous à java.util.en parallèle et java.util.simultané.atomic paquets aussi pour d'autres constructions de programmation.

 5
Author: Ravindra babu, 2017-08-22 08:36:50

Je pense qu'il y a une bonne explication sur la raison pour laquelle chacune d'entre elles sont des techniques vitales à votre actif dans un livre intitulé Java Concurrency In Practice de Brian Goetz. Il fait un point très clair - vous devez utiliser le même verrou "PARTOUT" pour protéger l'état de votre objet. Méthode synchronisée et synchronisation sur un objet vont souvent de pair. Par exemple, Vector synchronise toutes ses méthodes. Si vous avez un handle à un objet vectoriel et que vous allez faire "put if absent", alors simplement la synchronisation vectorielle ses propres méthodes individuelles ne vont pas vous protéger de la corruption de l'État. Vous devez synchroniser en utilisant synchronised (vectorHandle). Cela entraînera l'acquisition du MÊME verrou par chaque thread qui a une poignée pour le vecteur et protégera l'état global du vecteur. C'est ce qu'on appelle le verrouillage côté client. Nous savons en fait que vector synchronise (this) / synchronise toutes ses méthodes et donc que la synchronisation sur l'objet vectorHandle entraînera correctement synchronisation de l'état des objets vectoriels. Il est insensé de croire que vous êtes sûr de thread juste parce que vous utilisez une collection de 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 verrouillage côté client.
  2. Si vous avez un objet de verrouillage privé - cela rend le verrouillage côté client impossible. C'est bien si vous savez que votre classe ne le fait pas avoir" mettre si absent " type de fonctionnalité.
  3. Si vous concevez une bibliothèque - alors synchroniser sur cela ou synchroniser la méthode est souvent plus sage. Parce que vous êtes rarement en mesure de décider comment votre classe va être utilisée.
  4. Si Vector avait utilisé un objet de verrouillage privé - il aurait été impossible d'obtenir le droit "put if absent". Le code client ne gagnera jamais une poignée au verrou privé brisant ainsi la règle fondamentale d'utiliser EXACTEMENT le MÊME VERROU pour protéger son état.
  5. La synchronisation sur cette ou les méthodes synchronisées ont un problème comme d'autres l'ont souligné - quelqu'un pourrait obtenir un verrou et ne jamais le relâcher. Tous les autres threads continueraient à 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 objet de verrouillage privé vous donne une meilleure granularité - par exemple, si deux opérations ne sont pas liées - elles pourraient être gardées par des verrous différents, ce qui entraînerait un meilleur débit. Mais je pense que c'est une odeur de conception et non une odeur de code - si deux opérations sont complètement indépendantes, pourquoi font-elles partie de la MÊME classe? Pourquoi un club de classe ne devrait-il pas du tout avoir de fonctionnalités? Peut-être une classe utilitaire? Hmmmm-certains util fournissant une manipulation de chaîne et un formatage de date de calendrier via la même instance?? ... ça n'a aucun sens pour moi au moins!!
 4
Author: Sandesh Sadhale, 2014-11-04 09:24:36

Non, vous ne devriez 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 par rapport à 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 n'a pas besoin d'empêcher l'autre d'être écrit/lu. (En pratique, j'éviterais cela en déclarant les champs volatils et/ou en utilisant java.util.AtomicFoo du concurrent wrapper).

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 autour des autres. L'utilisation de synchronized(this)est encore plus maladroite et antisociale, car elle dit "personne ne peut changer quoi que ce soit sur cette classe pendant que je tiens la serrure". À quelle fréquence avez-vous réellement besoin de le faire?

Je préférerais avoir des verrous plus granulaires; même si vous voulez tout arrêter de changer (peut-être que vous sérialisez l'objet), vous pouvez simplement acquérir tous les verrous pour réaliser la même chose, plus c'est plus explicite de cette façon. Lorsque vous utilisez synchronized(this), on ne sait pas exactement pourquoi vous synchronisez, ni quels pourraient être les effets secondaires. Si vous utilisez synchronized(labelMonitor), ou encore mieux labelLock.getWriteLock().lock(), il est clair ce que vous faites et à quoi les effets de votre section critique sont limités.

 3
Author: Andrzej Doyle, 2009-01-14 10:50:44

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 essayer d'éviter synchronize(this) 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 des instances et lire sur ce sujet. (Qui a dit: juste à l'aide 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 réponse à utiliser synchronize(this) ou non vient naturellement.

 3
Author: tcurdt, 2009-01-14 11:04:53

Un verrou est utilisé soit pour visibilitéou pour protéger certaines données contre modification simultanée qui peut conduire à la course.

Lorsque vous devez simplement rendre les opérations de type primitif atomiques, il existe 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 les coordonnées x et y, qui sont liés l'un à l'autre et devraient être modifiés de manière atomique. Alors vous protégeriez l'utilisation d'un même verrou.

Un verrou ne doit protéger que l'état qui est lié l'un à l'autre. Rien de moins et rien de 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 à un conflit, même en mettant à jour un é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 seule méthode qui mute à la fois x et y et non deux méthodes différentes car x et y sont liées et si j'avais donné deux méthodes différentes pour muter x et y séparément, il n'aurait pas été thread safe.

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 rendre 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 verrous différents car l'État n'est pas lié l'un à l'autre.

Le processus d'utilisation différents verrous pour protéger les états non liés sont appelés Lock Striping ou Lock Splitting

 2
Author: Narendra Pathai, 2013-11-20 07:43:42

La raison de ne pas synchroniser sur this est que parfois vous avez besoin de plus d'un verrou (le deuxième verrou est souvent supprimé après une réflexion supplémentaire, mais vous en avez toujours besoin dans l'état intermédiaire). Si vous verrouillez sur this, vous devez toujours vous rappeler lequel des deux verrous est this; si vous verrouillez sur un objet privé, le nom de la variable vous le dit.

Du point de vue du lecteur, si vous voyez verrouiller ce , vous devez toujours répondre les deux questions:

  1. ce type d'accès est protégé par ce?
  2. un verrou suffit-il 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 verrous; quand il est temps d'invoquer l.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 l'impasse en utilisant deux serrures, une pour les opérations courtes et une pour les opérations longues.

 1
Author: 18446744073709551615, 2013-02-20 06:26:52

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 "this". Et bien sûr, vous pouvez manipuler avec des zones de votre fonction qui doivent être synchronisées et ainsi de suite.

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 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 le cas de la synchronisation l'utilisation du bloc doit être allouée à une variable locale qui contient une référence à"this". Et par conséquent, nous aurons une taille de fonction un peu plus grande (non pertinente si vous n'avez que peu de fonctions).

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 point de vue suivant:

Le mot-clé synchronisé est très limité en un zone: lors de la sortie d'un bloc synchronisé, tous les threads qui attendent ce verrou doivent être débloqués, mais un seul de ces threads peut prendre le verrou; tous les autres voient que le verrou est pris et reviennent à l'état bloqué. Ce n'est pas seulement beaucoup de cycles de traitement gaspillés: souvent, le changement de contexte pour débloquer un thread implique également la pagination de la mémoire sur le disque, et c'est très, très, cher.

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

 1
Author: Roman, 2014-03-13 16:35:40

Un bon exemple pour une 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 synchronize sur ce pour coopérer facilement de manière longitudinale (é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 avec l'utilisation de synchronized on private field. Mais parfois, lorsque nous avons déjà une conception avec des méthodes synchronisées(c'est-à-dire une classe héritée, dont nous dérivons, synchronized (this) peut être la seule solution).

 0
Author: Bart Prokop, 2010-07-13 13:19:27

Cela dépend de la tâche que vous voulez faire, mais je ne l'utiliserais pas. Vérifiez également si le thread-save-ness que vous souhaitez accompagner n'a pas pu être fait en synchronisant(ceci) en premier lieu? Il y a aussi quelques beaux verrous dans l'API qui pourraient vous aider:)

 0
Author: Harald Schilly, 2016-07-17 17:08:33

Je pense que les points un (quelqu'un d'autre utilisant votre verrou) et deux (toutes les méthodes utilisant le même verrou 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 gravé dans le marbre, c'est surtout une question de bonnes pratiques et de prévention des erreurs.

 -3
Author: Yoni Roit, 2009-01-14 10:43:12