synchronisé et reentrantlock
Dans la programmation multi-thread, nous utilisons des verrous lorsque le code doit être synchronisé. Java nous fournit deux méthodes de synchronisation: verrouillage intégré (synchronisé) et verrouillage explicite (reentrantlock). Des verrous explicites ont été introduits par JDK1.5. Quelles sont les similitudes et les différences entre ces deux serrures? Est-ce juste une option ou y a-t-il une autre raison? Cet article le découvrira.
// Exemple d'utilisation de mots clés synchronisée public void synchronisé add (int t) {// Méthode synchronisée this.v + = t;} public static synchronisé void sub (int t) {// Valeur de méthode statique synchronisée - = t;} public int décrémentAndAndget () {Synchronized (obj) {// Synchronized Block Retour de comité --v; }}Il s'agit d'utiliser des verrous intégrés, vous l'avez appris.
Le verrou intégré est très pratique à utiliser et ne nécessite pas d'acquisition et de libération explicites. Tout objet peut être utilisé comme serrure intégrée. L'utilisation de verrous intégrés peut résoudre la plupart des scénarios de synchronisation. "Tout objet peut être utilisé comme verrouillage intégré" signifie également que lorsque le mot-clé synchronisé apparaît, il y a un objet qui lui est associé. Spécifiquement:
Lorsque la synchronisation agit sur la méthode normale, l'objet de verrouillage est le suivant;
Lorsque la synchronisation agit sur la méthode statique, l'objet de verrouillage est l'objet de classe de la classe actuelle;
Lorsque Synchronisé agit sur le bloc de code, l'objet de verrouillage est cet OBJ en synchronisé (OBJ).
Verrouillage explicite
Le verrou intégré est si facile à utiliser, pourquoi avez-vous besoin d'un verrouillage explicite supplémentaire? Parce que certaines choses ne peuvent pas être faites avec des serrures intégrées, comme:
Nous voulons ajouter un temps d'attente à la serrure et abandonner avant l'obtention du délai d'attente, afin que nous n'attendons pas l'infini;
Nous voulons acquérir le verrou de manière interruptible, afin que le fil externe nous envoie un signal d'interruption et puisse évoquer le fil en attendant le verrou;
Nous voulons maintenir plusieurs files d'attente pour les serrures, comme une file d'attente de producteur et une file d'attente de consommateurs, tout en améliorant l'efficacité des serrures.
Les verrous explicites (ReentrantLocks) sont officiellement nés pour résoudre ces besoins flexibles. Reentrantlock signifie littéralement un verrouillage réentrant, et réentrant signifie que le thread peut demander le même verrouillage plusieurs fois en même temps sans le faire être bloqué par son impasse. Voici la différence entre un verrou intégré et un verrou explicite:
CHIMÉable: RenentrantLock.trylock (temps de temps long, unité TimeUnit) fournit un moyen de mettre fin au temps d'attente. Si le thread n'obtient pas le verrou dans le délai spécifié, la méthode retournera fausse et termine le thread en attente.
Interruptible: vous devez avoir vu InterruptedException. De nombreuses méthodes multi-threads lanceront cette exception. Cette exception n'est pas un fardeau causé par un défaut, mais une nécessité ou une bonne chose. L'interruptibilité nous offre un moyen d'obtenir un fil pour se terminer tôt (plutôt que d'avoir à attendre la fin de l'exécution du fil), ce qui est très utile pour les tâches qui prennent du temps. Pour les verrous intégrés, le fil attendra le verrou intégré s'il ne peut pas l'obtenir. Il n'y a pas d'autre moyen de mettre fin à l'attente, sauf pour acquérir la serrure. Rentrantlock.lockinterruply () nous donne un moyen de terminer l'attente avec une interruption.
File d'attente de condition: une fois qu'un thread a acquis un verrou, il peut entrer dans un état d'attente car il attend qu'une certaine condition se produise (le verrouillage intégré passe par la méthode object.wait (), et le verrouillage explicite passe par la méthode condition.Await ()). Le thread entrant dans l'état d'attente accrochera et libèrera automatiquement le verrou, et ces threads seront placés dans la file d'attente de conditions. Synchronisé n'a qu'une seule file d'attente conditionnelle, tandis que ReentrantLoc peut avoir plusieurs files d'attente conditionnelles. Quels sont les avantages de plusieurs files d'attente? Veuillez lire.
Prédication conditionnelle: Une fois qu'un thread a acquis une serrure, il doit parfois attendre qu'une certaine condition soit remplie avant de pouvoir faire les choses. Par exemple, le producteur doit attendre que le cache ne soit pas satisfait avant de pouvoir mettre des messages dans la file d'attente, tandis que le consommateur doit attendre que le cache ne soit pas vide avant qu'il ne puisse récupérer les messages de la file d'attente. Ces conditions sont appelées prédicats conditionnels. Le fil doit d'abord acquérir le verrou, puis déterminer si le prédicat conditionnel est satisfait. S'il n'est pas satisfait, il ne sera pas exécuté vers le bas. Le thread correspondant abandonnera le droit d'exécution et relâchera automatiquement le verrou. Différents threads utilisant le même verrou peuvent avoir des prédicats conditionnels différents. S'il n'y a qu'une seule file d'attente conditionnelle, lorsqu'un prédicat conditionnel est satisfait, il est impossible de déterminer quel thread dans la file d'attente conditionnelle pour se réveiller; Mais si chaque prédicat conditionnel a une file d'attente conditionnelle séparée, lorsqu'un conditionnel est satisfait, nous savons que le fil de la file d'attente correspondant doit être éveillé (le verrou intégré est éveillé via l'objet.notify () ou object.notifyall (), et le verrouillage explicite est éveillé via la méthode condition.signal () ou condition.signalall ()). C'est l'avantage de plusieurs files d'attente conditionnelles.
Lorsque vous utilisez des verrous intégrés, l'objet lui-même est à la fois un verrou et une file d'attente conditionnelle; Lorsque vous utilisez des verrous explicites, l'objet de RenentrantLock est un verrou et la file d'attente conditionnelle est obtenue via la méthode RenentrantLock.NewCondition (). Plusieurs files d'attente conditionnelles peuvent être obtenues en appelant cette méthode plusieurs fois.
Un exemple typique de l'utilisation d'un verrou explicite est le suivant:
// Exemple d'utilisation de verrouillage explicite ReentrantLock Lock = new reentrantLock (); // acquérir Lock, c'est l'utilisation correspondant au mot clé synchronisé. lock.lock (); essayez {// votre code} enfin {lock.unlock ();} // vous pouvez le temps et abandonner le verrou si vous obtenez le verrou une fois l'heure spécifiée dépassée. essayez {lock.trylock (10, timeunit.seconds); essayez {// votre code} enfin {lock.unlock (); }} catch (InterruptedException e1) {// exception maniement} // Vous pouvez interrompre, et le fil de fil peut être interrompu en attendant que le verrou soit acquis, essayez {lock.lockinterruply (); essayez {// votre code} enfin {lock.unlock (); }} catch (InterruptedException e) {// exception maniement} // plusieurs files d'attente d'attente, veuillez vous référer à [ArrayBlockingQueue] (https://github.com/Carpenterlee/jCrecopes/Blob/master/markdown/ArrayBlockingQueue.md) / ** Condition pour l'attente * / Prince Final condition NotEmpTy = Lock.NewContion (condition d'attente pour l'attente pour la condition finale NotEmpTy = Lock.NewContion;) put * / condition finale privée notull = lock.newCondition ();Notez que le code ci-dessus place unlock () dans un bloc final, ce qui est nécessaire. Un verrou explicite ne sera pas automatiquement libéré comme un verrou intégré. Lors de l'utilisation d'un verrou explicite, il doit être libéré manuellement dans le bloc final. Si le verrou n'est pas libéré en raison d'une exception après avoir acquis le verrou, alors le verrou ne sera jamais libéré! Mettez unlock () dans Block enfin pour vous assurer qu'il peut être libéré normalement quoi qu'il arrive.
en conclusion
Les verrous intégrés peuvent résoudre la plupart des scénarios qui nécessitent une synchronisation. Ce n'est que lorsque une flexibilité supplémentaire est requise, les verrous explicites doivent être pris en compte, tels que le synchronisation, l'interruption et les files d'attente d'attente multiples.
Bien que les verrous explicites soient flexibles, ils nécessitent une application et une libération explicites, et la libération doit être placée dans le bloc final, sinon la serrure ne peut jamais être libérée en raison d'exceptions! C'est l'inconvénient le plus évident du verrouillage explicite.
En résumé, lors de la synchronisation, veuillez donner la priorité à des serrures implicites plus sûres et plus faciles à utiliser.