Synchronisation vs verrouillage
java.util.concurrent
API fournit une classe appelée Lock
, qui serait essentiellement sérialiser le contrôle afin d'accéder à la ressource critique. Il donne la méthode telle que park()
et unpark()
.
nous pouvons faire des choses similaires si nous pouvons utiliser synchronized
mot-clé et en utilisant wait()
et notify() notifyAll()
méthodes.
je me demande laquelle est la meilleure dans la pratique et pourquoi?
10 réponses
si vous verrouillez simplement un objet, je préfère utiliser synchronized
exemple:
Lock.acquire();
doSomethingNifty(); // Throws a NPE!
Lock.release(); // Oh noes, we never release the lock!
vous devez faire explicitement try{} finally{}
partout.
alors qu'avec synchronisé, c'est super clair et impossible de se tromper:
synchronized(myObject) {
doSomethingNifty();
}
qui dit, Lock
s peut être plus utile pour les choses plus compliquées où vous ne pouvez pas acquérir et libérer d'une telle manière propre. Je préférerait honnêtement éviter d'utiliser Lock
s nu en premier lieu, et juste aller avec un contrôle de concurrence plus sophistiqué comme un CyclicBarrier
ou un 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 bonnes.
je me demande laquelle est la meilleure dans la pratique et pourquoi?
j'ai trouvé que Lock
et Condition
(et d'autres nouvelles classes concurrent
) sont juste plus d'outils pour la boîte à outils. Je pouvais faire presque tout ce dont j'avais besoin avec mon vieux marteau à pinces (le mot-clé synchronized
), mais il était difficile à utiliser dans certaines situations. Plusieurs de ces situations embarrassantes sont devenues beaucoup plus simples une fois que j'ai ajouté plus d'outils à ma boîte à outils: un caoutchouc un maillet, un marteau, un prybar, et quelques clous. cependant , mon vieux marteau à griffe voit encore sa part d'utilisation.
Je ne pense pas que l'un est vraiment" meilleur " que l'autre, mais plutôt chacun est un meilleur ajustement pour des problèmes différents. En un mot, le modèle simple et la nature orientée vers la portée de synchronized
aide à me protéger des bugs dans mon code, mais ces mêmes avantages sont parfois des obstacles dans des scénarios plus complexes. Sa ces plus complexe scénarios que le paquet concurrent a été créé pour aider à résoudre. Mais l'utilisation de ces constructions de haut niveau nécessite une gestion plus explicite et plus soigneuse dans le code.
= = =
je pense que le JavaDoc fait un bon travail de décrire la distinction entre Lock
et synchronized
(l'emphase est la mienne):
Verrouiller les implémentations de fournir plus vaste opérations de verrouillage que l'on peut obtenir en utilisant des méthodes et des instructions synchronisées. Ils permettent structuration plus flexible , peut avoir des propriétés tout à fait différentes, et peut support multiple associated Condition objects .
...
L'utilisation de synchronisé méthodes ou déclarations fournit l'accès à la serrure de moniteur implicite associée à chaque objet, mais force toutes l'acquisition et la libération de l'écluse à se produire dans un bloc de manière structurée : quand serrures multiples sont acquis 1519280920 "ils doivent être libérés dans l'ordre inverse , et tous les serrures doivent être libérées dans la même portée lexicale que celle dans laquelle elles ont été acquises .
alors que le mécanisme d'établissement de la portée pour "synchronisé méthodes et énoncés rend beaucoup plus facile de programmer avec les serrures de moniteur , et aide éviter de nombreuses erreurs de programmation communes impliquant des serrures, il ya les occasions où vous devez travailler avec des serrures d'une manière plus souple. Par exemple, * *certains algorithmes* pour traverser simultanément les structures de données nécessitent l'utilisation de "main-sur-main" ou "chaîne de verrouillage" : vous acquérez la serrure du noeud A, puis le noeud B, puis la libération de A et d'acquérir C, puis la libération de B et d'acquérir D et ainsi de suite. Implémentations des Lock interface permettent l'utilisation de telles techniques par permettant l'acquisition et la libération d'une serrure dans des portées différentes , et permettant l'acquisition et la libération de plusieurs serrures dans n'importe quel ordre .
avec ce une plus grande flexibilité vient une responsabilité supplémentaire . Le absence de le verrouillage par bloc supprime la libération automatique des serrures qui se produit avec des méthodes et des instructions synchronisées. Dans la plupart des cas, l'idiome suivant doit être utilisé:
...
lorsque le verrouillage et le déverrouillage se produisent dans des portées différentes , il faut prendre soin de "s'assurer que que tout le code qui est exécuté alors que le la serrure est maintenue est protégée par "essayer-enfin" ou "essayer-attraper à s'assurer que la serrure est libérée si nécessaire.
Verrouiller les implémentations de fournir des fonctionnalités supplémentaires sur l'utilisation de la synchronisation des méthodes et des déclarations en fournissant un non-tentative de blocage pour acquérir une écluse (tryLock()), une tentative de acquérir la écluse qui peut être interrompue (lockInterruptibly(), et une tentative de acquérir la écluse qui peut temporiser (tryLock(long, TimeUnit)).
...
vous pouvez réaliser tout ce que les utilitaires en java.util.simultané faire avec le faible niveau des primitives comme synchronized
, volatile
, ou attendre / notifier
cependant, la concurrence est délicate, et la plupart des gens obtiennent au moins certaines parties erronées, ce qui rend leur code soit incorrect ou inefficace (ou les deux).
L'API concurrente fournit une approche de plus haut niveau, qui est plus facile (et donc plus sûr) à utiliser. En un mot, vous ne devriez plus avoir besoin d'utiliser synchronized, volatile, wait, notify
directement.
la classe Lock elle-même est au niveau inférieur de cette boîte à outils, vous ne pouvez même pas avoir besoin d'utiliser directement non plus (vous pouvez utiliser Queues
et sémaphore et des trucs, etc, la plupart du temps).
Il existe 4 principaux facteurs pourquoi vous voulez utiliser synchronized
ou java.util.concurrent.Lock
.
Note: le verrouillage synchronisé est ce que je veux dire quand je dis verrouillage intrinsèque.
-
quand Java 5 est sorti avec ReentrantLocks, ils se sont avérés avoir tout à fait un noticeble débit différence puis verrouillage intrinsèque. Si vous cherchez un verrouillage plus rapide mécanisme et sont en cours d'exécution de 1,5 envisager j.u.c.ReentrantLock. Java 6 intrinsèque le verrouillage est maintenant comparable.
-
J. U. C. La serrure a différents mécanismes pour le verrouillage. Verrouillage interruptable - tenter de verrouiller jusqu'au verrouillage le fil est interrompu; verrouillage chronométré - tenter de verrouiller pour une certaine quantité et si vous n'avez pas succès; tryLock-tentative de verrouillage, si un autre fil tient le la serrure abandonne. Tout cela est inclus à part la simple serrure. Offre de verrouillage intrinsèque uniquement simple verrouillage
- Style. Si 1 et 2 ne tombent pas en catégories de ce que vous êtes concernées par la plupart des gens, y compris moi-même, serait de trouver la semenatiques de verrouillage intrinsèques plus faciles à lire et moins verbeux alors j.u.c.Serrure de verrouillage.
- Conditions Multiples. Un objet vous verrouillage ne peut être notifié et attendu pour un seul cas. Verrouillage la méthode newCondition permet seule Serrure avoir plusieurs raisons attendre ou signaler. Je n'ai pas encore besoin de cette fonctionnalité dans pratique, mais est une belle caractéristique pour ceux qui en ont besoin.
je voudrais ajouter quelques choses en plus de Bert f réponse.
Locks
supportent diverses méthodes de contrôle des serrures à grain plus fin, qui sont plus expressives que les moniteurs implicites ( synchronized
serrures)
une serrure fournit un accès exclusif à une ressource partagée: un seul thread à la fois peut acquérir la serrure et tout accès à la ressource partagée nécessite que la serrure soit acquise premier. Cependant, certains verrous peuvent permettre l'accès simultané à une ressource partagée, comme le verrou en lecture d'un ReadWriteLock.
Avantages Lock " Synchronisation à partir de la documentation page
-
l'utilisation de méthodes ou d'énoncés synchronisés permet d'accéder au moniteur implicite de verrouillage associé à chaque objet, mais force l'acquisition de toutes les serrures et libération devant se produire d'une manière structurée par blocs
-
Verrouiller les implémentations de fournir des fonctionnalités supplémentaires par rapport à l'utilisation de la synchronisation des méthodes et des déclarations en fournissant un non-tentative de blocage pour acquérir un
lock (tryLock())
, une tentative d'acquérir le verrou qui peut être interrompue (lockInterruptibly()
, et une tentative d'acquérir le verrou qui peuttimeout (tryLock(long, TimeUnit))
. -
une classe de serrure peut également fournir le comportement et la sémantique est très différente de celle du verrouillage implicite du moniteur, tel que commande Garantie, utilisation non réentrante ,ou Détection de blocage
ReentrantLock : en termes simples selon mon interprétation, ReentrantLock
permet à un objet de rentrer d'une section critique à une autre section critique . Puisque vous avez déjà verrouillé pour entrer une section critique, vous pouvez d'autres critiques section sur le même objet en utilisant la serrure courante.
ReentrantLock
caractéristiques clés selon cet article
- capacité de verrouiller de façon interruptible.
- capacité de temporiser en attendant le verrouillage.
- puissance pour créer une serrure juste.
- API pour obtenir la liste des threads en attente de verrouillage.
- flexibilité pour essayer de verrouiller sans blocage.
vous pouvez utiliser ReentrantReadWriteLock.ReadLock, ReentrantReadWriteLock.WriteLock
pour acquérir le contrôle sur le verrouillage granulaire sur les opérations de lecture et d'écriture.
mis à part ces trois boucles de rentrée, java 8 fournit une serrure de plus
StampedLock:
Java 8 navires avec un nouveau type de serrure appelé StampedLock qui prennent également en charge la lecture et l'écriture des serrures comme dans l'exemple ci-dessus. Dans contrairement à ReadWriteLock les méthodes de verrouillage d'un timbré renvoient un timbré représenté par une valeur longue.
vous pouvez utiliser ces timbres pour libérer une serrure ou pour vérifier si la serrure est toujours valide. De plus, les serrures timbrées supportent un autre mode de verrouillage appelé verrouillage optimiste.
Avoir un oeil à cette article sur l'utilisation de différents types de ReentrantLock
et StampedLock
verrous.
la principale différence est l'équité, en d'autres termes, les demandes sont-elles traitées par L'IFOP ou peut-il y avoir marchandage? La synchronisation du niveau de la méthode garantit une répartition équitable ou FIFO de la serrure.
synchronized(foo) {
}
ou
lock.acquire(); .....lock.release();
ne garantit pas l'équité.
si vous avez beaucoup de contestations pour la serrure, vous pouvez facilement rencontrer le marchandage où les requêtes plus récentes obtiennent la serrure et les requêtes plus anciennes sont bloquées. J'ai vu des cas où 200 threads arrivent en peu de temps pour une serrure et le 2ème pour arriver a été traité en dernier. C'est ok pour certaines applications, mais pour d'autres, c'est mortel.
voir le livre de Brian Goetz, "Java Competiency In Practice", section 13.3, pour une discussion complète de ce sujet.
Livre de Brian Goetz, "Java Competiency In Practice", section 13.3: "...Comme le verrouillage de rentrée par défaut, le verrouillage intrinsèque n'offre aucune garantie déterministe d'équité, mais le les garanties d'équité statistique de la plupart des implémentations de verrouillage sont suffisantes pour presque toutes les situations..."
facilite la vie des programmeurs. Voici quelques situations qui peuvent être atteints plus facilement avec serrure.
- verrouiller une méthode, et libérer la serrure dans une autre méthode.
- vous avez deux threads travaillant sur deux morceaux de code différents, cependant le premier thread il y a une dépendance sur le deuxième thread pour compléter un certain morceau de code avant qu'il ne continue plus (tandis que d'autres threads travaillent aussi simultanément). Un verrou partagé peut résoudre ce problème très facilement.
- la mise en Œuvre de moniteurs. Par exemple, une file d'attente simple où les méthodes put et get sont exécutées à partir de nombreux threads différents. Toutefois, voulez-vous sur les genoux ni les mêmes méthodes, l'une sur l'autre, ni les deux méthodes put et get peuvent se chevaucher. Dans ce cas, une serrure privée rend la vie plus facile.
tandis que, la serrure, et les conditions sont construits sur le synchronisé. Donc, vous pouvez certainement accomplir la même chose objectif avec qui. Cependant, cela pourrait rendre votre vie difficile et peut vous détourner de la résolution du problème réel.
la principale différence entre serrure et synchronisé est - avec les serrures, vous pouvez libérer et acquérir les serrures dans n'importe quel ordre. - synchronisé, vous pouvez libérer les verrous seulement dans l'ordre qu'il a acquis.
Lock and synchronize block sert le même but, mais cela dépend de l'usage. 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 fil entre le synchroniser bloc, l'autre bloc est également verrouillé. S'il y a plusieurs blocs synchrones sur le même objet, tous les blocs sont verrouillés. Dans de telles situations , java.util.simultané.Serrure peut être utilisé pour empêcher le verrouillage non désiré des blocs