Quelqu'un peut-il me dire l'avantage de la méthode synchronisée sur le bloc synchronisé avec un exemple?
Quelqu'un peut-il me dire l'avantage de la méthode synchronisée sur le bloc synchronisé avec un exemple?
Réponses:
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
}
synchronized
bloc est implémenté à l'aide de deux instructions monitorenter
et monitorexit
, plus un gestionnaire d'exceptions qui garantit qu'il monitorexit
est appelé même dans le cas exceptionnel. Tout cela est enregistré lors de l'utilisation d'une synchronized
méthode.
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.
Avantages:
Les inconvénients:
Avantages:
Les inconvénients:
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.
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.
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.
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
}
}
}
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.
monitorenter
et monitorexit
avant d'exécuter le code.
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;
}
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.
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);
}
}
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
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 Collection
mêmes Vector
et Hashtable
ils sont synchronisés quand ArrayList
ou HashMap
ne 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
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 synchronied
blocs. 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' Lock
API
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:
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.
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
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
}
}
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 synchronized
mé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é.
List
peut entraîner des problèmes de performances s'il existe un journal de code qui n'a pas nécessairement besoin d'être synchronisé
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.
À 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
TLDR; synchronized
N'utilisez ni le modificateur ni l' synchronized(this){...}
expression, mais synchronized(myLock){...}
où se myLock
trouve un champ d'instance final contenant un objet privé.
La différence entre l'utilisation du synchronized
modificateur sur la déclaration de méthode et l' synchronized(..){ }
expression dans le corps de la méthode est la suivante:
synchronized
modificateur spécifié sur la signature de la méthode
synchronized(this) { .... }
, etthis
objet 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.synchronized(...){...}
expression vous permet
Cependant, l'utilisation du synchronized
modificateur ou synchronized(...) {...}
avec this
comme 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 synchronized
modificateur ni l' synchronized(...)
expression conjointement avec this
comme 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
}
}
}
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:
L'initialisation paresseuse est possible.
Il est sûr pour les threads.
Les inconvénients:
- 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:
L'initialisation paresseuse est possible.
Il est également sûr pour les threads.
Les performances sont réduites car le mot clé synchronisé est dépassé.
Les inconvénients:
La première fois, cela peut affecter les performances.
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/
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