Y a-t-il un avantage à utiliser une méthode synchronisée au lieu d'un bloc synchronisé?

401

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

guerrier
la source
1
@cletus cette question est entièrement différente de stackoverflow.com/questions/442564/…
Yukio Fukuzawa

Réponses:

431

Quelqu'un peut-il me dire l'avantage de la méthode synchronisée sur le bloc synchronisé avec un exemple? Merci.

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

Peut-être que le seul (mais je n'appellerais pas cela un avantage) est que vous n'avez pas besoin d'inclure la référence de l'objet this.

Méthode:

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

Bloquer:

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

Voir? Aucun avantage du tout.

Les blocs n'ont des avantages par rapport aux méthodes bien, la plupart du temps en flexibilité parce que vous pouvez utiliser un autre objet comme verrou alors que la synchronisation de la méthode bloquerait l'objet entier.

Comparer:

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

contre.

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

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

De plus, si la méthode se développe, vous pouvez toujours garder 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
}
OscarRyz
la source
44
Un avantage pour le consommateur de l'API est que l'utilisation du mot clé synchronized dans la déclaration de méthode déclare également explicitement que la méthode se synchronise sur l'instance d'objet et est (vraisemblablement) thread-safe.
Scrubbie
59
Je sais que c'est une vieille question mais la synchronisation sur "ceci" est considérée dans certains cercles comme un anti-modèle. La conséquence involontaire est qu'en dehors de la classe, quelqu'un peut verrouiller une référence d'objet égale à "this" et empêcher d'autres threads de passer les barrières au sein de la classe, créant potentiellement une situation de blocage. Création d'un "objet final privé = nouvel objet ();" variable uniquement à des fins de verrouillage est la solution souvent utilisée. Voici une autre question directement liée à ce problème.
justin.hughey
30
"alors que la synchronisation de la méthode verrouillerait la classe complète." Ce n'est pas correct. Il ne verrouille pas la classe complète, mais l'instance complète. Plusieurs objets de la même classe possèdent tous leur propre verrou. :) Salue
codepleb
4
Quelque chose d'intéressant à ce sujet est que l'utilisation d'une méthode synchronisée fera que le bytecode généré aura 1 instruction de moins, car les méthodes ont un bit synchronisé intégré dans leur signature. Étant donné que la longueur du bytecode est un facteur déterminant si une méthode est alignée, le déplacement du bloc vers la signature de la méthode pourrait être la différence de décision. En théorie en tout cas. Je ne baserais pas une décision de conception sur la sauvegarde d'une seule instruction de bytecode, ce qui semble être une idée terrible. Mais encore, il est une différence. =)
corsiKa
2
@corsiKa: vous enregistrez plusieurs instructions. Un synchronizedbloc est implémenté à l'aide de deux instructions monitorenteret monitorexit, plus un gestionnaire d'exceptions qui garantit qu'il monitorexitest appelé même dans le cas exceptionnel. Tout cela est enregistré lors de l'utilisation d'une synchronizedméthode.
Holger
139

La seule vraie différence est qu'un bloc synchronisé peut choisir sur quel objet il se synchronise. Une méthode synchronisée peut uniquement utiliser 'this'(ou l'instance de classe correspondante pour une méthode de classe synchronisée). Par exemple, ceux-ci sont sémantiquement équivalents:

synchronized void foo() {
  ...
}

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

Ce dernier est plus flexible car il peut rivaliser pour le verrou associé à n'importe quel objet, souvent une variable membre. Il est également plus granulaire car vous pourriez avoir du code simultané en cours d'exécution 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 simultané dans des méthodes non synchronisées distinctes. Utilisez celui qui rend le code plus compréhensible.

jcrossley3
la source
Ce dernier peut également avoir du mérite si tout le code dans foo () n'a pas besoin d'être synchronisé.
Evan
1
C'est vrai, mais pas ce que "Warrior" a demandé: "L'avantage de la méthode synchronisée" il n'y en a pas.
OscarRyz
76

Méthode synchronisée

Avantages:

  • Votre IDE peut indiquer les méthodes synchronisées.
  • La syntaxe est plus compacte.
  • Force à diviser les blocs synchronisés en méthodes distinctes.

Les inconvénients:

  • Se synchronise avec cela et permet ainsi aux étrangers de s'y synchroniser aussi.
  • Il est plus difficile de déplacer du code en dehors du bloc synchronisé.

Bloc synchronisé

Avantages:

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

Les inconvénients:

  • La syntaxe est plus compliquée et rend donc 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 nécessitant une synchronisation. Cette classe doit être aussi petite que possible et il doit donc être facile de revoir la synchronisation. D'autres ne devraient pas avoir à se soucier de la synchronisation.

iny
la source
Quand vous dites "restez dans la classe", voulez-vous dire "restez dans l' objet ", ou est-ce que je manque quelque chose?
OldPeculier
36

La principale différence est que si vous utilisez un bloc synchronisé, vous pouvez verrouiller un objet autre que celui- ci, ce qui permet d'être beaucoup plus flexible.

Supposons que vous ayez une file d'attente de messages et plusieurs producteurs et consommateurs de messages. 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 on crée 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
}

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

cdecker
la source
2
Votre exemple se limite aux lectures non destructives. Si la lecture supprime le message de la file d'attente, cela échouera si cela est fait au moment où un producteur écrit dans la file d'attente.
ceving
30

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 appellent des méthodes synchronisées pour le même bloc d'objet (suspendre l'exécution) jusqu'à ce que le premier thread soit terminé avec l'objet.

Deuxièmement, lorsqu'une méthode synchronisée se termine, elle établit automatiquement une relation passe-avant avec tout appel ultérieur d'une méthode synchronisée pour le même objet. Cela garantit que les modifications de l'état de l'objet sont visibles pour tous les threads.

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

Instruction synchronisée

Contrairement aux méthodes synchronisées, les instructions synchronisées doivent spécifier l'objet qui fournit le verrou intrinsèque: le plus souvent, je l'utilise 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: Verrous intrinsèques et synchronisation La synchronisation est construite autour d'une entité interne connue sous le nom de verrou intrinsèque ou verrou de moniteur. (La spécification API fait souvent référence à cette entité simplement comme un "moniteur".) Les verrous intrinsèques jouent un rôle dans les deux aspects de la synchronisation: en imposant un accès exclusif à l'état d'un objet et en établissant des relations avant-événement qui sont essentielles à la visibilité.

Chaque objet a un verrou intrinsèque qui lui est associé. Par convention, un thread qui a besoin d'un accès exclusif et cohérent aux champs d'un objet doit acquérir le verrou intrinsèque de l'objet avant d'y accéder, puis libérer le verrou intrinsèque lorsqu'il en a fini avec eux. Un thread est censé posséder le verrou intrinsèque entre le moment où il a acquis le verrou et l'a libéré. Tant qu'un thread possède un verrou intrinsèque, aucun autre thread ne peut acquérir le même verrou. L'autre thread se bloquera lorsqu'il tentera 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();
    }
}

Vérification croisée des différentes sorties avec méthode synchronisée, bloc et sans synchronisation.

sudheer
la source
10
+1 pour être le seul à ce jour à mentionner que les constructeurs ne peuvent pas être synchronisés . Autrement dit, dans un constructeur, vous n'avez vraiment qu'une seule option: les blocs synchronisés.
ef2011
J'ai testé votre code comme indiqué mais C est toujours 0, puis -2024260031 et la seule chose qui change le code de hachage. Quel comportement doit être observé?
Justin Johnson
Vous devriez avoir cité ci-dessous les articles dont le contenu a été fourni: docs.oracle.com/javase/tutorial/essential/concurrency/… et docs.oracle.com/javase/tutorial/essential/concurrency/…
Ravindra babu
29

Remarque: les méthodes et les blocs synchronisés statiques fonctionnent sur l'objet Class.

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

   // similar
   public static void foo() {
      synchronized(MyClass.class) {
// do something
      }
   }
}
Peter Lawrey
la source
18

Lorsque 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_SYNCHRONIZED défini, puis elle acquiert automatiquement le verrou de l'objet, appelle la méthode et libère le verrou. Si une exception se produit, le thread libère automatiquement le verrou.

La synchronisation d'un bloc de méthode, d'autre part, contourne la prise en charge intégrée de la JVM pour l'acquisition du verrouillage d'objet et de la gestion des exceptions et nécessite que la fonctionnalité soit explicitement écrite en code octet. Si vous lisez le code d'octet d'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 les 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 synchronizedMethodGet()méthode génère le code d'octet suivant:

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

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

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 significative entre la méthode synchronisée et le bloc est que, le bloc synchronisé réduit généralement la portée du verrouillage. Comme la portée du verrouillage est inversement proportionnelle aux performances, il est toujours préférable de verrouiller uniquement la section critique du code. L'un des meilleurs exemples d'utilisation de bloc synchronisé est le verrouillage à double vérification dans le modèle Singleton où, au lieu de verrouiller la getInstance()méthode entière , nous verrouillons uniquement la section critique du code qui est utilisée pour créer une instance Singleton. Cela améliore considérablement les performances car le verrouillage n'est requis qu'une ou deux fois.

Lorsque vous utilisez des méthodes synchronisées, vous devrez faire très attention si vous mélangez des méthodes synchronisées synchronisées statiques et non statiques.

Mohammad Adil
la source
1
Si nous regardons la méthode synchronisée bytecode, le bytecode est plus compact et simple, alors pourquoi ce n'est pas plus rapide que le bloc synchronisé?
eatSleepCode
@eatSleepCode Notez qu'il s'agit de bytecode qui est ensuite "compilé" par la JVM. La JVM ajoutera le nécessaire monitorenteret monitorexitavant d'exécuter le code.
Philip Couling
12

Le plus souvent, je l'utilise 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.

Dans le code suivant, un thread modifiant la liste ne bloquera pas l'attente d'un thread qui modifie la carte. Si les méthodes étaient synchronisées sur l'objet, chaque méthode devrait attendre, même si les modifications qu'elles apportent n'entreraient 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;
}
Clint
la source
7

Avec les blocs synchronisés, vous pouvez avoir plusieurs synchroniseurs, de sorte que plusieurs choses simultanées mais non conflictuelles puissent continuer en même temps.

Paul Tomblin
la source
6

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 du modèle sont synchronisées .

L'extrait de code 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);
        }
}
Kojotak
la source
5

Remarque importante sur l'utilisation du bloc synchronisé: faites attention à ce que vous utilisez comme objet de verrouillage!

L'extrait de code de user2277816 ci-dessus illustre ce point en ce qu'une référence à un littéral de chaîne est utilisée comme objet de verrouillage. Sachez que les littéraux de chaîne sont automatiquement internés en Java et vous devriez commencer à voir le problème: chaque morceau de code qui se synchronise sur le "verrou" littéral, partage le même verrou! Cela peut facilement conduire à des blocages avec des morceaux de code complètement indépendants.

Ce ne sont pas seulement les objets String avec lesquels vous devez faire attention. Les primitives encadrées sont également un danger, car les méthodes de zone automatique et les méthodes valueOf 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+reused

Søren Boisen
la source
5

Souvent, l'utilisation d'un verrou au niveau d'une méthode est trop grossière. Pourquoi verrouiller un morceau de code qui n'accède à aucune ressource partagée en verrouillant une méthode entière. Étant donné que chaque objet a un verrou, vous pouvez créer des objets factices pour implémenter la synchronisation au niveau du bloc. Le niveau de bloc est plus efficace car il ne verrouille pas toute la méthode.

Voici un exemple

Niveau de 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
 }
}

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
 }

[Éditer]

Pour les Collectionmêmes Vectoret Hashtableils sont synchronisés quand ArrayListou HashMapne le sont pas et vous devez définir un mot clé synchronisé ou invoquer la méthode synchronisée des collections:

Map myMap = Collections.synchronizedMap (myMap); // single lock for the entire map
List myList = Collections.synchronizedList (myList); // single lock for the entire list
Maxim Shoustin
la source
5

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

Fondamentalement synchronized , des méthodes ou des blocs ont été utilisés pour écrire du code thread-safe 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 nouvelles constructions de programmation ont été introduites pour la sécurité des threads.

Vous pouvez garantir la sécurité des threads en utilisant une API de concurrence avancée au lieu de synchroniedblocs. 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.

Un meilleur remplacement pour synchronisé est ReentrantLock , qui utilise l' LockAPI

Un verrou d'exclusion mutuelle réentrant avec le même comportement de base et la même sémantique que le verrou de moniteur implicite accessible à l'aide de méthodes et d'instructions 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()
     }
   }
 }

Reportez-vous à java.util.concurrent et java.util.concurrent.atomic packages pour d'autres constructions de programmation.

Reportez-vous également à cette question connexe:

Synchronisation vs verrouillage

Ravindra babu
la source
4

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

kishore
la source
3

En général, ce sont essentiellement les mêmes, sauf qu'elles sont explicites sur le moniteur de l'objet utilisé par rapport à l'implicite de cet objet. Un inconvénient des méthodes synchronisées qui, je pense, est parfois négligé est qu'en utilisant la référence "this" pour se synchroniser, vous laissez ouverte la possibilité de verrouillage d'objets externes 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 tout autre champ existant peut éviter ce problème, encapsulant complètement la synchronisation.

Alex Miller
la source
2

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. 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 un peu plus grande pour la fonction (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

romain
la source
2

En cas de méthodes synchronisées, le verrouillage sera acquis sur un objet. Mais si vous optez pour un bloc synchronisé, vous avez la possibilité de spécifier un objet sur lequel le verrou sera acquis.

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
   } 

   }
Srinu Yarru
la source
2

Je sais que c'est une vieille question, mais avec ma lecture rapide des réponses ici, je n'ai vraiment vu personne mentionner que parfois une synchronizedméthode peut être le mauvais verrou.
Extrait de Java Concurrency 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 thread-safe. Cependant, en réalité, ce n'est pas le cas. Dans ce cas, le verrou est obtenu sur l'instance de la classe. Cependant, il est possible que la liste soit modifiée par un autre thread 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 empêcherait tous les threads essayant de modifier la liste de modifier la liste jusqu'à ce que le bloc synchronisé soit terminé.

aarbor
la source
lire ce livre en ce moment ... je me demande ... si cette liste était privée au lieu de publique et n'avait que la méthode putIfAbsent, synchronisé (ce) serait suffisant non? le problème est que la liste peut également être modifiée en dehors de ListHelper?
dtc
@dtc oui si la liste était privée et ne fuyait nulle part ailleurs dans la classe, cela serait suffisant, tant que vous avez marqué toutes les autres méthodes de la classe qui modifient également la liste comme synchronisée. Cependant, verrouiller toute la méthode au lieu de simplement la Listpeut entraîner des problèmes de performances s'il existe un journal de code qui n'a pas nécessairement besoin d'être synchronisé
aarbor
ça a du sens. merci beaucoup d'avoir répondu! tbh, j'ai trouvé le livre très utile pour élargir mes connaissances et comment aborder le multithreading, mais cela m'a aussi introduit un tout nouveau monde de confusion
dtc
2

En 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 un littéral de chaîne ou verrouiller le contenu d'un champ modifiable qui est modifié sous les threads.

D'un autre côté, avec les méthodes synchronisées, vous ne pouvez pas protéger le verrou d'être acquis par un thread qui peut obtenir une référence à l'objet.

Ainsi, l'utilisation de synchronized comme modificateur sur les méthodes est meilleure 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 meilleure pour protéger votre propre code contre les cow-orkers.

Nathan Hughes
la source
1

À partir d'un résumé des spécifications Java: http://www.cs.cornell.edu/andru/javaspec/17.doc.html

L'instruction synchronisée (§14.17) calcule une référence à un objet; il tente ensuite d'effectuer une action de verrouillage sur cet objet et ne poursuit pas tant que l'action de verrouillage n'est pas terminée avec succès. ...

Une méthode synchronisée (§8.4.3.5) exécute automatiquement une action de verrouillage lorsqu'elle est invoquée; son corps n'est pas exécuté tant que l'action de verrouillage n'est pas terminée avec succès. Si la méthode est une méthode d'instance , elle verrouille le verrou associé à l'instance pour laquelle elle a été invoquée (c'est-à-dire l'objet qui sera connu comme cela lors de l'exécution du corps de la méthode). Si la méthode est statique , elle verrouille le verrou associé à l'objet Class qui représente la classe dans laquelle 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 sinon à trouver comment obtenir "l'objet Class qui représente la classe dans laquelle la méthode était défini. "

Edit: Je pensais à l'origine qu'il s'agissait de citations de la spécification Java réelle. Clarifié que cette page n'est qu'un résumé / explication de la spécification

Josiah Yoder
la source
1

TLDR; synchronizedN'utilisez ni le modificateur ni l' synchronized(this){...}expression, mais synchronized(myLock){...}où se myLocktrouve un champ d'instance final contenant un objet privé.


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

  • Le synchronizedmodificateur spécifié sur la signature de la méthode
    1. est visible dans le JavaDoc généré,
    2. est déterminable par programmation via la réflexion lors du test du modificateur d'une méthode pour Modifier.SYNCHRONIZED ,
    3. nécessite moins de frappe et d'indentation par rapport à synchronized(this) { .... }, et
    4. (en fonction de votre IDE) est visible dans le plan de classe et l'achèvement du code,
    5. utilise l' thisobjet comme verrou lorsqu'il est déclaré sur une méthode non statique ou la classe englobante lorsqu'il est déclaré sur une méthode statique.
  • L' synchronized(...){...}expression vous permet
    1. pour synchroniser uniquement l'exécution de parties du corps d'une méthode,
    2. à utiliser dans un constructeur ou un bloc d'initialisation ( statique ),
    3. pour choisir l'objet verrou qui contrôle l'accès synchronisé.

Cependant, l'utilisation du synchronizedmodificateur ou synchronized(...) {...}avec thiscomme objet de verrouillage (comme dans synchronized(this) {...}) présente le même inconvénient. Les deux utilisent sa propre instance comme objet de verrouillage pour se synchroniser. Ceci est dangereux car 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 verrou de synchronisation avec des effets secondaires potentiellement graves (dégradation des performances et blocages ).

Par conséquent, la meilleure pratique consiste à ne pas utiliser le synchronizedmodificateur ni l' synchronized(...)expression conjointement avec thiscomme objet de verrouillage, mais un objet de verrouillage privé de 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 une attention particulière doit être prise pour garantir que cela n'entraîne pas de blocages lorsqu'ils sont 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
        }
    }
}
Sebastian Thomschke
la source
1

Je suppose que cette question concerne la différence entre l' initialisation Thread Safe Singleton et lazy avec verrouillage Double check . Je me réfère toujours à cet article lorsque j'ai besoin d'implémenter un singleton spécifique.

Eh bien, ceci est un Singleton Thread Safe :

// Java program to create Thread Safe 
// Singleton class 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

 //synchronized method to control simultaneous access 
  synchronized public static GFG getInstance()  
  { 
    if (instance == null)  
    { 
      // if instance is null, initialize 
      instance = new GFG(); 
    } 
    return instance; 
  } 
} 

Avantages:

  1. L'initialisation paresseuse est possible.

  2. Il est sûr pour les threads.

Les inconvénients:

  1. La méthode getInstance () est synchronisée, ce qui ralentit les performances car plusieurs threads ne peuvent pas y accéder simultanément.

Il s'agit d'une initialisation paresseuse avec verrouillage à double vérification :

// Java code to explain double check locking 
public class GFG  
{ 
  // private instance, so that it can be 
  // accessed by only by getInstance() method 
  private static GFG instance; 

  private GFG()  
  { 
    // private constructor 
  } 

  public static GFG getInstance() 
  { 
    if (instance == null)  
    { 
      //synchronized block to remove overhead 
      synchronized (GFG.class) 
      { 
        if(instance==null) 
        { 
          // if instance is null, initialize 
          instance = new GFG(); 
        } 

      } 
    } 
    return instance; 
  } 
} 

Avantages:

  1. L'initialisation paresseuse est possible.

  2. Il est également sûr pour les threads.

  3. Les performances sont réduites car le mot clé synchronisé est dépassé.

Les inconvénients:

  1. La première fois, cela peut affecter les performances.

  2. Par contre. de la méthode de verrouillage à double contrôle est supportable, elle peut donc être utilisée pour des applications multithread haute performance.

Veuillez vous référer à cet article pour plus de détails:

https://www.geeksforgeeks.org/java-singleton-design-pattern-practices-examples/

indéfini
la source
-3

Synchronisation avec les threads. 1) N'utilisez JAMAIS synchronisé (ceci) dans un thread cela ne fonctionne pas. La synchronisation avec (this) utilise le thread actuel comme objet de thread de verrouillage. Puisque chaque thread est indépendant des autres threads, il n'y a PAS de coordination de synchronisation. 2) Les tests de code montrent qu'en Java 1.6 sur Mac, la synchronisation de méthode ne fonctionne pas. 3) synchronisé (lockObj) où lockObj est un objet partagé commun de tous les threads qui se synchronisent dessus fonctionnera. 4) ReenterantLock.lock () et .unlock () fonctionnent. Voir les tutoriels Java pour cela.

Le code suivant montre ces points. Il contient également le vecteur thread-safe qui serait substitué à ArrayList, pour montrer que de nombreux threads s'ajoutant à un vecteur ne perdent aucune information, tandis que le même avec un ArrayList peut perdre des informations. 0) Le code actuel montre une perte d'informations en raison des conditions de concurrence A) Commentez la ligne A actuellement étiquetée et décommentez la ligne A au-dessus, puis exécutez, la méthode perd des données, mais elle ne devrait pas. B) Inversez l'étape A, décommentez B et // bloc de fin}. Ensuite, exécutez pour voir les résultats sans perte de données C) Commentez B, décommentez C. Exécutez, voir la synchronisation sur (ceci) perd les données, comme prévu. N'ayez pas le temps de terminer toutes les variantes, j'espère que cela vous aidera. Si la synchronisation sur (ceci) ou la synchronisation de méthode fonctionne, veuillez indiquer la version de Java et du système d'exploitation que vous avez testée. Je vous remercie.

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
user2277816
la source
1
Synchronisation avec « (ce) » fait le travail, et ne pas « utiliser le thread courant comme l'objet de synchronisation », à moins que l'objet actuel d'une classe qui étend cette discussion. -1
Marquis de Lorne