Est-il un avantage à utiliser une Méthode Synchronisée au lieu d'un Bloc Synchronisé?

quelqu'un Peut-il me dire l'avantage de la méthode synchronisée sur bloc synchronisé avec un exemple?

373
demandé sur Mahozad 2009-02-22 06:13:48

22 réponses

quelqu'un Peut me dire l'avantage de la méthode synchronisée sur le bloc synchronisé avec un exemple? Grâce.

il n'y a pas d'avantage évident à utiliser la méthode synchronisée sur le bloc.

peut-être le seul ( mais je ne dirais pas que c'est un avantage ) est que vous n'avez pas besoin d'inclure la référence d'objet this .

méthode:

public synchronized void method() { // blocks "this" from here.... 
    ...
    ...
    ...
} // to here

bloc:

public void method() { 
    synchronized( this ) { // blocks "this" from here .... 
        ....
        ....
        ....
    }  // to here...
}

voir? Aucun avantage.

blocs do ont des avantages sur les méthodes cependant, surtout en flexibilité parce que vous pouvez utiliser un autre objet comme serrure alors que la synchronisation de la méthode verrouillerait l'objet entier.

comparer:

// locks the whole object
... 
private synchronized void someInputRelatedWork() {
    ... 
}
private synchronized void someOutputRelatedWork() {
    ... 
}

vs.

// Using specific locks
Object inputLock = new Object();
Object outputLock = new Object();

private void someInputRelatedWork() {
    synchronized(inputLock) { 
        ... 
    } 
}
private void someOutputRelatedWork() {
    synchronized(outputLock) { 
        ... 
    }
}

aussi si la méthode se développe, vous pouvez encore gardez la section synchronisée séparée:

 private void method() {
     ... code here
     ... code here
     ... code here
    synchronized( lock ) { 
        ... very few lines of code here
    }
     ... code here
     ... code here
     ... code here
     ... code here
}
397
répondu OscarRyz 2018-07-14 15:15:51

la seule différence réelle est qu'un bloc synchronisé peut choisir sur quel objet il se synchronise. Une méthode synchronisée ne peut utiliser que 'this' (ou l'instance de classe correspondante pour une méthode de classe synchronisée). Par exemple, ils sont sémantiquement équivalents:

synchronized void foo() {
  ...
}

void foo() {
    synchronized (this) {
      ...
    }
}

ce dernier est plus flexible puisqu'il peut rivaliser pour la serrure associée de n'importe quel objet, souvent une variable membre. C'est aussi plus granulaire parce que vous pouvez avoir du code concurrent exécuté avant et après le bloc mais toujours dans la méthode. Bien sûr, vous pouvez tout aussi facilement utiliser une méthode synchronisée en refactorisant le code concurrent en méthodes non synchronisées séparées. Utilisez celui qui rend le code plus compréhensible.

136
répondu jcrossley3 2009-02-22 03:46:39

Méthode Synchronisée

Pour:

  • votre IDE peut indiquer les méthodes synchronisées.
  • la syntaxe est plus compacte.
  • Forces pour diviser les blocs synchronisés pour séparer les méthodes.

Inconvénients:

  • synchronise à ceci et permet ainsi aux étrangers de se synchroniser à lui aussi.
  • il est plus difficile de déplacer le code en dehors du bloc synchronisé.

bloc synchronisé

Pour:

  • permet d'utiliser une variable privée pour la serrure et donc forcer la serrure à rester à l'intérieur de la classe.
  • blocs synchronisés peuvent être trouvés en recherchant des références à la variable.

Inconvénients:

  • la syntaxe est plus compliquée et donc rend le code plus difficile à lire.

personnellement, je préfère utiliser des méthodes synchronisées avec des classes axées uniquement sur la chose à synchroniser. Une telle classe devrait être aussi petite que possible et donc il devrait être facile de revoir la synchronisation. Les autres ne devraient pas avoir besoin de se soucier de la synchronisation.

75
répondu iny 2010-09-09 19:22:00

La différence principale est que si vous utilisez un bloc synchronisé vous pouvez verrouiller sur un objet autre que ce qui permet d'être beaucoup plus souple.

supposons que vous avez une file d'attente de messages et plusieurs producteurs de messages et consommateurs. Nous ne voulons pas que les producteurs interfèrent les uns avec les autres, mais les consommateurs devraient pouvoir récupérer les messages sans avoir à attendre les producteurs. Donc nous créons juste un objet

Object writeLock = new Object();

et à partir de Maintenant chaque fois qu'un producteur veut ajouter un nouveau message, nous nous en tenons à cela:

synchronized(writeLock){
  // do something
}

donc les consommateurs peuvent toujours lire, et les producteurs seront verrouillés.

35
répondu cdecker 2013-09-19 10:44:44

méthode synchronisée

les méthodes synchronisées ont deux effets.

Tout d'abord, lorsqu'un thread exécute une méthode synchronisée pour un objet, tous les autres threads qui invoquent des méthodes synchronisées pour le même bloc d'objet (suspendre l'exécution) jusqu'à ce que le premier thread soit fait avec l'objet.

Deuxièmement, lorsqu'une méthode synchronisée sort, elle établit automatiquement une relation happens-before toutes les invocation d'une méthode synchronisée pour le même objet. Cela garantit que les modifications de l'état de l'objet sont visibles par tous les threads.

notez que les constructeurs ne peuvent pas être synchronisés - utiliser le mot-clé synchronisé avec un constructeur est une erreur de syntaxe. Synchroniser des constructeurs n'a pas de sens, car seul le thread qui crée un objet doit y avoir accès pendant sa construction.

Déclaration Synchronisée

Contrairement aux méthodes synchronisées, les instructions synchronisées doivent spécifier l'objet qui fournit le verrouillage intrinsèque: le plus souvent j'utilise ceci pour synchroniser l'accès à une liste ou une carte mais je ne veux pas bloquer l'accès à toutes les méthodes de l'objet.

Q: serrures intrinsèques et synchronisation La synchronisation est construite autour d'une entité interne connue sous le nom de serrure intrinsèque ou serrure de moniteur. (La spécification API fait souvent référence cette entité simplement comme un "moniteur.") Les serrures intrinsèques jouent un rôle dans les deux aspects de la synchronisation: imposer l'accès exclusif à l'état d'un objet et établir des happens-avant les relations qui sont essentielles à la visibilité.

tout objet possède une serrure intrinsèque qui lui est associée. Par convention, un thread qui a besoin d'un accès exclusif et cohérent aux champs d'un objet doit acquérir la serrure intrinsèque de l'objet avant d'y accéder, puis libérer le verrouillage intrinsèque quand il est fait avec eux. Un fil est dit posséder la serrure intrinsèque entre le moment où il a acquis la serrure et libéré la serrure. Tant qu'un thread possède une serrure intrinsèque, aucun autre thread ne peut acquérir la même serrure. L'autre fil se bloquer lorsqu'il tente d'acquérir le verrou.

package test;

public class SynchTest implements Runnable {  
    private int c = 0;

    public static void main(String[] args) {
        new SynchTest().test();
    }

    public void test() {
        // Create the object with the run() method
        Runnable runnable = new SynchTest();
        Runnable runnable2 = new SynchTest();
        // Create the thread supplying it with the runnable object
        Thread thread = new Thread(runnable,"thread-1");
        Thread thread2 = new Thread(runnable,"thread-2");
//      Here the key point is passing same object, if you pass runnable2 for thread2,
//      then its not applicable for synchronization test and that wont give expected
//      output Synchronization method means "it is not possible for two invocations
//      of synchronized methods on the same object to interleave"

        // Start the thread
        thread.start();
        thread2.start();
    }

    public synchronized  void increment() {
        System.out.println("Begin thread " + Thread.currentThread().getName());
        System.out.println(this.hashCode() + "Value of C = " + c);
//      If we uncomment this for synchronized block, then the result would be different
//      synchronized(this) {
            for (int i = 0; i < 9999999; i++) {
                c += i;
            }
//      }
        System.out.println("End thread " + Thread.currentThread().getName());
    }

//    public synchronized void decrement() {
//        System.out.println("Decrement " + Thread.currentThread().getName());
//    }

    public int value() {
        return c;
    }

    @Override
    public void run() {
        this.increment();
    }
}

contre-vérifier différentes sorties avec méthode synchronisée, bloc et sans synchronisation.

29
répondu sudheer 2011-11-07 16:15:36

Note: static méthodes synchronisées et blocs de travail sur l'objet de classe.

public class MyClass {
   // locks MyClass.class
   public static synchronized void foo() {
// do something
   }

   // similar
   public static void foo() {
      synchronized(MyClass.class) {
// do something
      }
   }
}
27
répondu Peter Lawrey 2009-02-22 03:30:31

quand le compilateur java convertit votre code source en code octet, il gère les méthodes synchronisées et les blocs synchronisés très différemment.

lorsque la JVM exécute une méthode synchronisée, le thread d'exécution identifie que la structure method_info de la méthode a le drapeau ACC_SYNCHRONISÉ, puis il acquiert automatiquement la serrure de l'objet, appelle la méthode, et libère la serrure. Si une exception se produit, le thread déclenche automatiquement la serrure.

synchroniser un bloc de méthode, d'autre part, contourne le support intégré de la JVM pour acquérir la manipulation de verrouillage et d'exception d'un objet et exige que la fonctionnalité soit explicitement écrite en code octet. Si vous lisez le code d'octets pour une méthode avec un bloc synchronisé, vous verrez plus d'une douzaine d'opérations supplémentaires pour gérer cette fonctionnalité.

cela montre des appels pour générer à la fois une méthode synchronisée et un bloc synchronisé:

public class SynchronizationExample {
    private int i;

    public synchronized int synchronizedMethodGet() {
        return i;
    }

    public int synchronizedBlockGet() {
        synchronized( this ) {
            return i;
        }
    }
}

la méthode synchronizedMethodGet() génère le code byte suivant:

0:  aload_0
1:  getfield
2:  nop
3:  iconst_m1
4:  ireturn

et voici le code d'octet de la méthode synchronizedBlockGet() :

0:  aload_0
1:  dup
2:  astore_1
3:  monitorenter
4:  aload_0
5:  getfield
6:  nop
7:  iconst_m1
8:  aload_1
9:  monitorexit
10: ireturn
11: astore_2
12: aload_1
13: monitorexit
14: aload_2
15: athrow

une différence importante entre la méthode synchronisée et le bloc est que le bloc synchronisé réduit généralement la portée de l'écluse. Comme la portée de la serrure est inversement proportionnelle à la performance, il est toujours préférable de verrouiller seulement la section critique du code. Un des meilleurs exemples de l'utilisation de bloc synchronisé est double contrôle de verrouillage dans le modèle Singleton où au lieu de la méthode de verrouillage entier getInstance() nous ne verrouiller la section critique du code qui est utilisé pour créer l'instance Singleton. Cela améliore considérablement les performances car le verrouillage n'est nécessaire qu'une ou deux fois.

tout en utilisant des méthodes synchronisées, vous aurez besoin de prendre des précautions supplémentaires si vous mélangez static synchronized et non-static synchronisé méthodes.

17
répondu Mohammad Adil 2014-04-23 08:05:58

le Plus souvent je l'utilise pour synchroniser l'accès à une liste ou sur la carte mais je ne veux pas bloquer l'accès à toutes les méthodes de l'objet.

dans le code suivant, un thread modifiant la liste ne sera pas bloqué en attendant un thread modifiant la carte. Si les méthodes étaient synchronisées sur l'objet alors chaque méthode devrait attendre même si les modifications qu'ils font ne serait pas en conflit.

private List<Foo> myList = new ArrayList<Foo>();
private Map<String,Bar) myMap = new HashMap<String,Bar>();

public void put( String s, Bar b ) {
  synchronized( myMap ) {
    myMap.put( s,b );
    // then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public void hasKey( String s, ) {
  synchronized( myMap ) {
    myMap.hasKey( s );
  }
}

public void add( Foo f ) {
  synchronized( myList ) {
    myList.add( f );
// then some thing that may take a while like a database access or RPC or notifying listeners
  }
}

public Thing getMedianFoo() {
  Foo med = null;
  synchronized( myList ) {
    Collections.sort(myList);
    med = myList.get(myList.size()/2); 
  }
  return med;
}
12
répondu Clint 2009-02-22 07:04:38

avec des blocs synchronisés, vous pouvez avoir plusieurs synchroniseurs, de sorte que plusieurs choses simultanées mais non-conflictuelles peuvent continuer en même temps.

6
répondu Paul Tomblin 2009-02-22 03:19:18
Les méthodes synchronisées

peuvent être vérifiées à l'aide de l'API de réflexion. Cela peut être utile pour tester certains contrats, tels que toutes les méthodes dans le modèle sont synchronisées .

l'extrait suivant imprime toutes les méthodes synchronisées de Hashtable:

for (Method m : Hashtable.class.getMethods()) {
        if (Modifier.isSynchronized(m.getModifiers())) {
            System.out.println(m);
        }
}
6
répondu Kojotak 2013-09-20 08:45:10

Note importante sur l'utilisation du bloc synchronisé: attention à ce que vous utilisez comme objet lock!

le code extrait de user2277816 ci-dessus illustre ce point en ce qu'une référence à une chaîne littérale est utilisée comme objet de verrouillage. Réalisez que la littérature des cordes est automatiquement internée en Java et vous devriez commencer à voir le problème: chaque morceau de code qui se synchronise sur le "lock" littéral, partage le même lock! Cela peut facilement conduire à des blocages avec complètement des morceaux de code sans rapport.

ce n'est pas seulement des objets String que vous devez faire attention. Les primitives en boîte sont également un danger, puisque l'auto-oxydation et la valeur des méthodes peuvent réutiliser les mêmes objets, selon la valeur.

Pour plus d'informations, voir: https://www.securecoding.cert.org/confluence/display/java/LCK01-J.+Do+not+synchronize+on+objects+that+may+be+réutilisé

5
répondu Søren Boisen 2013-08-12 11:41:31

souvent utiliser une serrure sur un niveau de méthode est trop rude. Pourquoi verrouiller un morceau de code qui n'accède à aucune ressource partagée en verrouillant une méthode entière. Puisque chaque objet a une serrure, vous pouvez créer des objets factices pour implémenter la synchronisation de niveau de bloc. le niveau du bloc est plus efficace parce qu'il ne bloque pas l'ensemble de la méthode.

voici quelques exemples

Niveau De La Méthode

class MethodLevel {

  //shared among threads
SharedResource x, y ;

public void synchronized method1() {
   //multiple threads can't access
}
public void synchronized method2() {
  //multiple threads can't access
}

 public void method3() {
  //not synchronized
  //multiple threads can access
 }
}

"151990920 De Niveau De Bloc

class BlockLevel {
  //shared among threads
  SharedResource x, y ;

  //dummy objects for locking
  Object xLock = new Object();
  Object yLock = new Object();

    public void method1() {
     synchronized(xLock){
    //access x here. thread safe
    }

    //do something here but don't use SharedResource x, y
    // because will not be thread-safe
     synchronized(xLock) {
       synchronized(yLock) {
      //access x,y here. thread safe
      }
     }

     //do something here but don't use SharedResource x, y
     //because will not be thread-safe
    }//end of method1
 }

[Modifier]

Pour Collection comme Vector et Hashtable ils sont synchronisés lorsque ArrayList ou HashMap ne le sont pas et vous avez besoin de définir synchronisé mot-clé ou d'appeler des Collections méthode synchronisée:

Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
5
répondu Maxim Shoustin 2013-10-10 11:41:40

méthode synchronisée est utilisé pour verrouiller tous les objets Le bloc synchronisé est utilisé pour verrouiller un objet spécifique

4
répondu kishore 2010-07-14 06:35:56

la seule différence: les blocs synchronisés permettent le verrouillage granulaire contrairement à la méthode synchronisée

fondamentalement synchronized bloc ou des méthodes ont été utilisés pour écrire le code de thread sûr en évitant les erreurs d'incohérence de mémoire.

cette question est très ancienne et beaucoup de choses ont changé au cours des 7 dernières années. De nouveaux concepts de programmation ont été introduits pour la sécurité des fils.

vous pouvez obtenir la sécurité du thread en utilisant l'API de concurrence avancée au lieu de synchronied blocs. Cette documentation page fournit de bonnes constructions de programmation pour atteindre la sécurité de filetage.

Lock Objects prise en charge 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.

Variables Atomiques ont des fonctionnalités qui minimisent la synchronisation et aider à é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.

meilleur remplacement pour synchronisé est ReentrantLock , qui utilise Lock API

A verrou d'exclusion mutuelle de rentrée avec le même comportement de base et la sémantique que le verrou de moniteur implicite accédé en utilisant des méthodes et des déclarations synchronisées, mais avec des capacités étendues.

exemple avec serrures:

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

Consultez de java.util.concurrent et java.util.simultané.les paquets atomiques aussi pour d'autres constructions de programmation.

se référer à ce question connexe trop:

synchronisation vs verrouillage

4
répondu Ravindra babu 2017-08-14 15:47:36

en général, ils sont généralement les mêmes, sauf qu'ils sont explicites au sujet du moniteur de l'objet qui est utilisé par rapport à l'implicite cet objet. Un inconvénient des méthodes synchronisées qui je pense est parfois négligé est qu'en utilisant la" cette " référence pour synchroniser sur vous laissez ouverte la possibilité d'objets externes se verrouiller sur le même objet. Cela peut être un bug très subtil si vous le rencontrez. La synchronisation sur un objet explicite interne ou un autre champ existant peut éviter ce numéro, encapsulant complètement la synchronisation.

3
répondu Alex Miller 2009-02-22 20:28:59

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. Mais tout le monde dit qu'il n'y a pas de différence entre fonction synchronisée et bloc qui couvre l'ensemble de 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. En cas d'utilisation de blocs synchronisés doit être attribuée variable locale qui détient une référence à "ceci". Et comme résultat, nous aurons un peu plus grande taille pour la fonction (non applicable si vous avez seulement quelques nombre de fonctions).

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

2
répondu Roman 2013-07-16 08:43:36

dans le cas de méthodes synchronisées, verrouillage sera acquis sur un objet. Mais si vous allez avec le bloc synchronisé vous avez une option pour spécifier un objet sur lequel la serrure sera acquise.

exemple:

    Class Example {
    String test = "abc";
    // lock will be acquired on String  test object.
    synchronized (test) {
        // do something
    }

   lock will be acquired on Example Object
   public synchronized void testMethod() {
     // do some thing
   } 

   }
2
répondu Srinu Yarru 2015-12-13 11:54:37

je sais que c'est une vieille question, mais avec ma lecture rapide des réponses ici, je n'ai pas vraiment vu personne mentionner que parfois une méthode synchronized peut être le faux serrure.

D'après Java Competiency In Practice(p. 72):

public class ListHelper<E> {
  public List<E> list = Collections.syncrhonizedList(new ArrayList<>());
...

public syncrhonized boolean putIfAbsent(E x) {
 boolean absent = !list.contains(x);
if(absent) {
 list.add(x);
}
return absent;
}

le code ci-dessus a l'apparence d'être sans fil. Cependant, en réalité il ne l'est pas. Dans ce cas, la serrure est obtenue sur l'instance du classe. Toutefois, il est possible que le liste soit modifié par un autre fil n'utilisant pas cette méthode. L'approche correcte serait d'utiliser

public boolean putIfAbsent(E x) {
 synchronized(list) {
  boolean absent = !list.contains(x);
  if(absent) {
    list.add(x);
  }
  return absent;
}
}

le code ci-dessus bloquerait tous les threads en essayant de modifier liste de modifier la liste jusqu'à ce que le bloc synchronisé soit terminé.

2
répondu aarbor 2017-01-25 17:04:07

d'un point de vue pratique, l'avantage des méthodes synchronisées par rapport aux blocs synchronisés est qu'elles sont plus résistantes aux idiots; parce que vous ne pouvez pas choisir un objet arbitraire à verrouiller, vous ne pouvez pas abuser de la syntaxe de la méthode synchronisée pour faire des choses stupides comme verrouiller sur une chaîne littérale ou verrouiller sur le contenu d'un champ mutable qui se change sous les threads.

d'un autre côté, avec des méthodes synchronisées vous ne pouvez pas protéger la serrure de obtenir acquis par n'importe quel thread qui peut obtenir une référence à l'objet.

donc utiliser synchronisé comme modificateur sur les méthodes est mieux pour protéger vos cow-orkers de se blesser, tandis que l'utilisation de blocs synchronisés en conjonction avec des objets de verrouillage final privés est mieux pour protéger votre propre code des cow-orkers.

2
répondu Nathan Hughes 2018-06-12 13:47:47

D'un résumé de spécification Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

la déclaration synchronisée (§14.17) calcule une référence à un objet; ensuite, il tente d'effectuer une action de verrouillage sur cet objet, et ne pas continuer jusqu'à ce que l'opération de verrouillage soit terminée. ...

Une méthode synchronisée (§8.4.3.5) effectue automatiquement une action de verrouillage quand il est invoquée; son corps n'est pas exécutée jusqu'à ce que l'action de verrouillage a complété avec succès. Si la méthode est une méthode d'instance , il verrouille la serrure associée à l'instance pour laquelle elle a été invoquée (c'est-à-dire l'objet qui sera connu lors de l'exécution de le corps de la méthode). si la méthode est statique , elle bloque la serrure associée à L'objet de classe qui représente la classe dans qui la méthode est définie. ...

sur la base de ces descriptions, je dirais que la plupart des réponses précédentes sont correctes, et une méthode synchronisée pourrait être particulièrement utile pour les méthodes statiques, où vous auriez autrement à comprendre comment obtenir l'objet de classe" qui représente la classe dans laquelle la méthode a été définie."

Edit: j'ai d'abord pensé que ces citations ont été de réels Java spec. A précisé que cette page est juste un résumé/explication de la spec

1
répondu Josiah Yoder 2013-12-19 13:15:08

TLDR; N'utilisez ni le modificateur synchronized ni l'expression synchronized(this){...} mais synchronized(myLock){...}myLock est un champ d'instance finale contenant un objet privé.


la différence entre l'utilisation du modificateur synchronized sur la déclaration de méthode et l'expression synchronized(..){ } dans le corps de la méthode est la suivante:

  • le modificateur synchronized spécifié le la signature de la méthode
    1. est visible dans le JavaDoc généré,
    2. est déterminable par programmation via réflexion lors de l'essai du modificateur d'une méthode pour modificateur.Synchronisé ,
    3. nécessite moins de Dactylographie et d'indentation que synchronized(this) { .... } , et
    4. (selon votre IDE) est visible dans le contour de la classe et l'achèvement du code,
    5. utilise l'objet this comme Serrure lorsqu'il est déclaré selon une méthode non statique ou la classe d'enveloppe lorsqu'il est déclaré selon une méthode statique.
  • l'expression synchronized(...){...} vous permet
    1. de ne synchroniser l'exécution d'une partie de la méthode du corps,
    2. à utiliser au sein d'un constructeur ou d'un bloc d'initialisation ( statique ),
    3. pour choisir le verrou de l'objet qui contrôle l'accès synchronisé.

cependant, l'utilisation du synchronized modificateur ou synchronized(...) {...} avec this comme l'objet de verrouillage (comme dans synchronized(this) {...} ), ont le même désavantage. Les deux utilisent sa propre instance comme objet de verrouillage pour se synchroniser. Cela est dangereux parce que non seulement l'objet lui-même, mais tout autre objet/code externe qui contient une référence à cet objet peut également l'utiliser comme une serrure de synchronisation avec des effets secondaires potentiellement graves (dégradation de la performance et deadlocks ).

par conséquent, la meilleure pratique n'est pas d'utiliser le modificateur synchronized ni l'expression synchronized(...) en conjonction avec this comme objet de verrouillage, mais un objet de verrouillage privé à cet objet. Par exemple:

public class MyService {
    private final lock = new Object();

    public void doThis() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }

    public void doThat() {
       synchronized(lock) {
          // do code that requires synchronous execution
        }
    }
}

vous pouvez également utiliser plusieurs objets de verrouillage, mais des soins spéciaux doit être prises pour s'assurer de ne pas entraîner des blocages lorsque utilisé imbriqués.

public class MyService {
    private final lock1 = new Object();
    private final lock2 = new Object();

    public void doThis() {
       synchronized(lock1) {
          synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThat() and doMore().
          }
    }

    public void doThat() {
       synchronized(lock1) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doMore() may execute concurrently
        }
    }

    public void doMore() {
       synchronized(lock2) {
              // code here is guaranteed not to be executes at the same time
              // as the synchronized code in doThis().
              // doThat() may execute concurrently
        }
    }
}
1
répondu Sebastian Thomschke 2017-08-15 22:12:36

synchronisant avec les fils. 1) N'utilisez jamais synchronized (ceci) dans un thread qu'il ne fonctionne pas. La synchronisation avec (ceci) utilise le thread courant comme objet de thread de verrouillage. Puisque chaque thread est indépendant des autres threads, il n'y a pas de coordination de la synchronisation. 2) Les Tests de code montrent qu'en Java 1.6 sur un Mac la synchronisation de méthode ne fonctionne pas. 3) synchronisé(lockObj) où lockObj est un objet commun partagé de tous les threads synchronisant sur elle fonctionnera. Quatre) ReenterantLock.lock() et .unlock (). Voir les tutoriels Java pour cela.

le code suivant indique ces points. Il contient également le vecteur thread-safe qui serait substitué à L'ArrayList, pour montrer que de nombreux fils ajoutant à un vecteur ne perdent aucune information, Alors que la même avec un ArrayList peut perdre des informations. 0) le code courant indique une perte d'information due aux conditions de course A) Commenter le courant étiqueté une ligne, et décommenter la ligne A au-dessus de lui, puis la méthode perd des données, mais elle ne devrait pas. B) marche arrière a, uncomment B et // end block }. Puis Exécuter pour voir les résultats aucune perte de données C) Commentaire de B, décommentez C. Exécuter, voir synchronisation (ce) perd des données, comme prévu. Ne pas avoir le temps de compléter toutes les variations, espérons que cela aide. Si la synchronisation sur (ceci), ou la méthode de synchronisation fonctionne, s'il vous plaît indiquer quelle version de Java et OS vous avez testé. Remercier.

import java.util.*;

/** RaceCondition - Shows that when multiple threads compete for resources 
     thread one may grab the resource expecting to update a particular 
     area but is removed from the CPU before finishing.  Thread one still 
     points to that resource.  Then thread two grabs that resource and 
     completes the update.  Then thread one gets to complete the update, 
     which over writes thread two's work.
     DEMO:  1) Run as is - see missing counts from race condition, Run severa times, values change  
            2) Uncomment "synchronized(countLock){ }" - see counts work
            Synchronized creates a lock on that block of code, no other threads can 
            execute code within a block that another thread has a lock.
        3) Comment ArrayList, unComment Vector - See no loss in collection
            Vectors work like ArrayList, but Vectors are "Thread Safe"
         May use this code as long as attribution to the author remains intact.
     /mf
*/ 

public class RaceCondition {
    private ArrayList<Integer> raceList = new ArrayList<Integer>(); // simple add(#)
//  private Vector<Integer> raceList = new Vector<Integer>(); // simple add(#)

    private String countLock="lock";    // Object use for locking the raceCount
    private int raceCount = 0;        // simple add 1 to this counter
    private int MAX = 10000;        // Do this 10,000 times
    private int NUM_THREADS = 100;    // Create 100 threads

    public static void main(String [] args) {
    new RaceCondition();
    }

    public RaceCondition() {
    ArrayList<Thread> arT = new ArrayList<Thread>();

    // Create thread objects, add them to an array list
    for( int i=0; i<NUM_THREADS; i++){
        Thread rt = new RaceThread( ); // i );
        arT.add( rt );
    }

    // Start all object at once.
    for( Thread rt : arT ){
        rt.start();
    }

    // Wait for all threads to finish before we can print totals created by threads
    for( int i=0; i<NUM_THREADS; i++){
        try { arT.get(i).join(); }
        catch( InterruptedException ie ) { System.out.println("Interrupted thread "+i); }
    }

    // All threads finished, print the summary information.
    // (Try to print this informaiton without the join loop above)
    System.out.printf("\nRace condition, should have %,d. Really have %,d in array, and count of %,d.\n",
                MAX*NUM_THREADS, raceList.size(), raceCount );
    System.out.printf("Array lost %,d. Count lost %,d\n",
             MAX*NUM_THREADS-raceList.size(), MAX*NUM_THREADS-raceCount );
    }   // end RaceCondition constructor



    class RaceThread extends Thread {
    public void run() {
        for ( int i=0; i<MAX; i++){
        try {
            update( i );        
        }    // These  catches show when one thread steps on another's values
        catch( ArrayIndexOutOfBoundsException ai ){ System.out.print("A"); }
        catch( OutOfMemoryError oome ) { System.out.print("O"); }
        }
    }

    // so we don't lose counts, need to synchronize on some object, not primitive
    // Created "countLock" to show how this can work.
    // Comment out the synchronized and ending {, see that we lose counts.

//    public synchronized void update(int i){   // use A
    public void update(int i){                  // remove this when adding A
//      synchronized(countLock){            // or B
//      synchronized(this){             // or C
        raceCount = raceCount + 1;
        raceList.add( i );      // use Vector  
//          }           // end block for B or C
    }   // end update

    }   // end RaceThread inner class


} // end RaceCondition outter class
-3
répondu user2277816 2013-04-14 16:31:04