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?
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, Lock
s 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 Lock
s 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.
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)).
...
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) .
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.
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.
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
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
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.
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))
.
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
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
.
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.
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 ... "
Lock facilite la vie des programmeurs. Voici quelques situations qui peuvent être réalisées plus facilement avec le verrouillage.
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.
Différence majeure entre verrouillé et synchronisé:
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.