web-dev-qa-db-fra.com

Synchronisation vs verrouillage

L'API Java.util.concurrent fournit une classe appelée Lock, qui sérialiserait le contrôle afin d'accéder à la ressource critique. Il donne une méthode telle que park() et unpark()

Nous pouvons faire la même chose si nous pouvons utiliser le mot clé synchronized et les méthodes wait() et notify() notifyAll()

Je me demande lequel d’entre eux est meilleur en pratique et pourquoi?

161
daydreamer

Si vous verrouillez simplement un objet, je préférerais utiliser synchronized

Exemple:

Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!

Vous devez explicitement faire try{} finally{} partout.

Alors qu'avec Synchronisé, c'est super clair et impossible de se tromper:

synchronized(myObject) {
    doSomethingNifty();
}

Cela dit, Locks peut être plus utile pour des choses plus complexes où vous ne pouvez pas acquérir et publier de manière aussi propre. Honnêtement, je préférerais éviter d’utiliser des variables Locks nues et opter pour un contrôle de concurrence plus sophistiqué, tel que CyclicBarrier ou LinkedBlockingQueue, s’ils répondent à vos besoins.

Je n'ai jamais eu de raison d'utiliser wait() ou notify() mais il peut y en avoir de bons.

166
Steven Schlansker

Je me demande lequel d’entre eux est meilleur en pratique et pourquoi?

J'ai trouvé que Lock et Condition (et d'autres nouvelles classes concurrent) ne sont que des outils supplémentaires pour la boîte à outils. Je pouvais faire à peu près tout ce dont j'avais besoin avec mon ancien marteau à griffe (le mot clé synchronized), mais il était difficile à utiliser dans certaines situations. Plusieurs de ces situations délicates sont devenues beaucoup plus simples une fois que j’ai ajouté d’autres outils à ma boîte à outils: un maillet en caoutchouc, un marteau à bille, un levier, et quelques coups de clou. Cependant , mon ancien marteau à griffes voit toujours sa part d'utilisation.

Je ne pense pas que l’un soit vraiment "meilleur" que l’autre, mais que chacun est mieux adapté à différents problèmes. En résumé, le modèle simple et la nature orientée portée de synchronized m'aident à me protéger contre les bogues dans mon code, mais ces mêmes avantages sont parfois des obstacles dans des scénarios plus complexes. Ce sont ces scénarios plus complexes pour lesquels le package simultané a été créé. Mais l'utilisation de ces constructions de niveau supérieur nécessite une gestion plus explicite et plus prudente du code.

===

Je pense que le JavaDoc décrit bien la distinction entre Lock et synchronized (l'accent est à moi):

Les implémentations de verrouillage fournissent des opérations de verrouillage plus étendues que celles pouvant être obtenues à l'aide de méthodes et d'instructions synchronisées. Ils permettent une structuration plus souple , peuvent avoir des propriétés très différentes et peuvent prend en charge plusieurs objets Condition associés .

...

L’utilisation de méthodes synchronisées permet d’accéder au verrou de surveillance implicite associé à chaque objet, mais force toute l'acquisition et la libération de verrous à se dérouler de manière structurée en blocs : when plusieurs verrous sont acquis ils doivent être libérés dans l'ordre inverse , et tous les verrous doivent être libérés dans le même champ lexical dans lequel ils ont été acquis .

Tandis que le mécanisme de cadrage pour les méthodes et les déclarations facilite beaucoup la programmation avec les verrous du moniteur , et aide à éviter de nombreux problèmes courants erreurs de programmation impliquant des verrous, il est parfois nécessaire de travailler avec des verrous de manière plus flexible. Par exemple, * * certains algorithmes * permettant de parcourir des structures de données simultanément utilisées nécessitent l'utilisation de "hand au-dessus de la main "ou" verrouillage de la chaîne ": vous acquérez le verrou du noeud A, puis du noeud B, puis relâchez A et acquérez C, puis relâchez B et acquérez D etc. Les implémentations de la interface de verrouillage permettent l’utilisation de telles techniques par permettant à un verrou d’être acquis et libéré dans différentes portées , et permettant d’acquérir et de libérer plusieurs verrous dans n’importe quel ordre .

Avec cela , une flexibilité accrue entraîne des responsabilités supplémentaires . L'absence de verrouillage structuré en blocs supprime la libération automatique des verrous qui se produit avec les méthodes et instructions synchronisées. . Dans la plupart des cas, l'idiome suivant devrait être utilisé:

...

Lorsque le verrouillage et le déverrouillage se produisent dans différentes portées , vous devez prendre soin de assure que tout le code exécuté pendant que le verrou est maintenu est protégé par try-finally ou try-catch à , afin de vous assurer que le verrou est libéré si nécessaire.

Les implémentations de verrouillage fournissent des fonctionnalités supplémentaires à l'utilisation de méthodes et d'instructions synchronisées en fournissant un tentative non bloquante d’acquérir un verrou (tryLock ()), tentative de acquiert le verrou qui peut être interrompu (lockInterruptibly (), et une tentative de acquiert le verrou qui peut expirer (tryLock (long, TimeUnit)).

...

64
Bert F

Vous pouvez obtenir tous les utilitaires de Java.util.concurrent do avec les primitives de bas niveau telles que synchronized, volatile ou wait / notify

Cependant, la concurrence est délicate et la plupart des gens se trompent, ce qui rend leur code incorrect ou inefficace (ou les deux).

L'API simultanée fournit une approche de niveau supérieur, plus facile à utiliser (et donc plus sûre). En un mot, vous ne devriez plus avoir besoin d'utiliser synchronized, volatile, wait, notify directement. 

La classe Lock elle-même se trouve dans la partie inférieure de cette boîte à outils, vous n’avez peut-être même pas besoin de l’utiliser directement non plus (vous pouvez utiliser Queues et Semaphore , etc., le plus souvent) .

21
Thilo

Pourquoi voudriez-vous utiliser synchronized ou Java.util.concurrent.Lock pour 4 raisons principales?.

Remarque: le verrouillage synchronisé est ce que je veux dire lorsque je parle de verrouillage intrinsèque. 

  1. Lorsque Java 5 est sorti avec ReentrantLocks, ils ont démontré que le débit de Était considérable, alors que le verrouillage était différent. Si vous recherchez un mécanisme de verrouillage plus rapide et que vous utilisez la version 1.5, considérez j.u.c.ReentrantLock. Le verrouillage intrinsèque de Java 6 est maintenant Comparable.

  2. j.u.c.Lock a différents mécanismes pour le verrouillage. Verrouillage interruptable - essayer de verrouiller jusqu'au verrouillage le fil est interrompu; verrou cadencé - essayer de verrouiller pour un certain montant de temps et d'abandonner si vous ne le faites pas réussir; tryLock - tentative de verrouillage, si un autre fil tient le verrouiller abandonner. Tout cela est inclus en dehors de la serrure simple. Le verrouillage intrinsèque n’offre que des possibilités simples verrouillage

  3. Style. Si 1 et 2 ne tombent pas en catégories de ce que vous êtes concerné par la plupart des gens, y compris moi-même, trouverais le la sémantique de verrouillage intrinsèque plus facile à lire et moins verbeux alors j.u.c.Lock lock.
  4. Conditions multiples. Un objet sur lequel vous verrouillez ne peut être notifié et attendu pour un seul cas. La méthode NewCondition de Lock permet à un seul verrou D'avoir plusieurs raisons d'attendre ou de signaler. Je n'ai pas encore Besoin de cette fonctionnalité pour Pratique, mais c'est une fonctionnalité intéressante pour Ceux qui en ont besoin.
14
John Vint

J'aimerais ajouter quelques mots à la réponse de {Bert F}.

Locks prend en charge diverses méthodes pour un contrôle de verrouillage à grain plus fin, qui sont plus expressives que les moniteurs implicites (synchronized verrous)

Un verrou fournit un accès exclusif à une ressource partagée: un seul thread à la fois peut acquérir le verrou et tout accès à la ressource partagée nécessite que le verrou soit acquis en premier. Toutefois, certains verrous peuvent autoriser l'accès simultané à une ressource partagée, telle que le verrou de lecture d'un ReadWriteLock.

Avantages de Lock over Synchronization from documentation page

  1. L'utilisation de méthodes ou d'instructions synchronisées permet d'accéder au verrou de surveillance implicite associé à chaque objet, mais force l'acquisition et la libération de tous les verrous d'une manière structurée en blocs.

  2. Les implémentations de verrou fournissent des fonctionnalités supplémentaires par rapport à l'utilisation de méthodes et d'instructions synchronisées en fournissant une tentative non bloquante d'acquérir une lock (tryLock()), une tentative pour obtenir le verrou pouvant être interrompu (lockInterruptibly()) et une tentative pour acquérir le verrou pouvant timeout (tryLock(long, TimeUnit)).

  3. Une classe Lock peut également fournir un comportement et une sémantique assez différents de ceux du verrouillage de moniteur implicite, tels que garantis, un usage non réentrant ou une détection d'interblocage.

ReentrantLock : En termes simples, selon ma compréhension, ReentrantLock permet à un objet de revenir dans une section critique à une autre section critique. Comme vous avez déjà le verrou pour entrer dans une section critique, vous pouvez utiliser d’autres sections critiques sur le même objet en utilisant le verrou actuel. 

ReentrantLock caractéristiques clés selon cet article article

  1. Possibilité de verrouiller de manière interruptible.
  2. Possibilité d'expiration en attendant le verrouillage.
  3. Le pouvoir de créer un verrou équitable.
  4. API pour obtenir la liste des threads en attente pour le verrouillage.
  5. Souplesse pour essayer de verrouiller sans bloquer.

Vous pouvez utiliser ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock pour acquérir davantage de contrôle sur le verrouillage granulaire des opérations de lecture et d'écriture.

En plus de ces trois verrous Reentrant, Java 8 fournit un verrou supplémentaire.

StampedLock:

Java 8 est livré avec un nouveau type de verrou appelé StampedLock, qui prend également en charge les verrous en lecture et en écriture, comme dans l'exemple ci-dessus. Contrairement à ReadWriteLock, les méthodes de verrouillage d'un StampedLock renvoient un tampon représenté par une valeur longue. 

Vous pouvez utiliser ces tampons pour libérer un verrou ou pour vérifier si le verrou est toujours valide. De plus, les verrous marqués prennent en charge un autre mode de verrouillage appelé verrouillage optimiste.

Jetez un oeil à cet article sur l'utilisation de différents types de verrous ReentrantLock et StampedLock.

5
Ravindra babu

La principale différence est l’équité, c’est-à-dire que les demandes sont traitées FIFO ou peut-il y avoir barge? La synchronisation au niveau de la méthode garantit une affectation juste ou FIFO du verrou. En utilisant 

synchronized(foo) {
}

ou

lock.acquire(); .....lock.release();

n'assure pas l'équité.

Si vous avez beaucoup de conflits au sujet du verrou, vous pouvez facilement rencontrer des barges où les demandes les plus récentes sont verrouillées et les demandes plus anciennes bloquées. J'ai vu des cas où 200 threads arrivent rapidement pour un verrou et le second arrivé a été traité en dernier. Cela convient pour certaines applications, mais pour d'autres, c'est mortel.

Reportez-vous à la section 13.3 du livre "Java Concurrency In Practice" de Brian Goetz pour une analyse complète de ce sujet.

4
Brian Tarbox

Le livre de Brian Goetz "Java Concurrency In Practice", section 13.3: "... Comme le ReentrantLock par défaut, le verrouillage intrinsèque n'offre aucune garantie d'équité déterministe, mais les garanties d'équité statistique de la plupart des implémentations de verrouillage sont assez bonnes toutes les situations ... "

3
bodrin

Lock facilite la vie des programmeurs. Voici quelques situations qui peuvent être réalisées plus facilement avec le verrouillage.

  1. Verrouillez une méthode et libérez le verrou dans une autre méthode. 
  2. Vous avez deux threads travaillant sur deux morceaux de code différents, cependant le premier thread dépend de son second thread pour terminer certains morceaux de code avant de continuer (alors que d'autres threads fonctionnent simultanément). Un verrou partagé peut résoudre ce problème assez facilement.
  3. Mise en place de moniteurs. Par exemple, une simple file d'attente dans laquelle les méthodes put et get sont exécutées à partir de nombreux threads différents. Toutefois, ne souhaitez-vous pas que les mêmes méthodes soient superposées, ni que les méthodes de vente et de vente ne puissent se chevaucher? Dans ce cas, un verrou privé rend la vie plus facile.

Alors que, le verrou et les conditions sont construits sur le synchronisé. Donc, vous pouvez certainement atteindre le même objectif avec cela. Cependant, cela pourrait vous rendre la vie difficile et vous empêcher de résoudre le problème. 

1
Md Monjur Ul Hasan

Différence majeure entre verrouillé et synchronisé:

  • avec des serrures, vous pouvez les libérer et les acquérir dans n’importe quel ordre.
  • avec synchronisé, vous ne pouvez libérer les verrous que dans l’ordre dans lequel ils ont été acquis.
0
NKumar

Verrouiller et synchroniser le bloc servent tous les deux le même but mais cela dépend de l'utilisation. Considérez la partie ci-dessous

void randomFunction(){
.
.
.
synchronize(this){
//do some functionality
}

.
.
.
synchronize(this)
{
// do some functionality
}


} // end of randomFunction

Dans le cas ci-dessus, si un thread entre dans le bloc de synchronisation, l'autre bloc est également verrouillé. S'il existe plusieurs blocs de synchronisation de ce type sur le même objet, tous les blocs sont verrouillés. Dans ce cas, Java.util.concurrent.Lock peut être utilisé pour empêcher le verrouillage indésirable de blocs.

0
Shyam