Quel est l'avantage d'utiliser l'interface/implémentations Condition par rapport au mécanisme de notification d'attente conventionnel? Je cite ici les commentaires écrits par Doug Lea:
Condition conditionne les méthodes de surveillance d'objet (attendre, notifier et notifier tout) en objets distincts pour donner l'effet d'avoir plusieurs ensembles d'attente par objet, en les combinant avec l'utilisation d'implémentations de verrouillage arbitraires. Lorsqu'un verrou remplace l'utilisation de méthodes et d'instructions synchronisées, une condition remplace l'utilisation des méthodes de surveillance d'objet.
Je vois que c'est une façon plus orientée objet de mettre en œuvre un mécanisme d'attente/notification. Mais y a-t-il un solide avantage sur le premier?
Il existe de nombreux avantages comme mentionné ci-dessus à propos de Interface de condition certains importants sont suit:
L'interface de condition est livrée avec Deux supplémentaires méthodes qui sont:
1) booléen waitUntil (Date deadline) lève InterruptedException: Fait attendre le thread actuel jusqu'à ce qu'il soit signalé ou interrompu, ou que le délai spécifié se soit écoulé.
2) waitUninterruptably (): Fait attendre le thread actuel jusqu'à ce qu'il soit signalé.
Si l'état interrompu du thread actuel est défini lorsqu'il entre dans cette méthode, ou s'il est interrompu en attendant, il continuera d'attendre jusqu'à ce qu'il soit signalé. Lorsqu'il revient enfin de cette méthode, son état interrompu sera toujours défini.
Les deux méthodes ci-dessus ne sont pas présentes dans le moniteur par défaut qui se trouve dans la classe d'objets, dans certaines situations, nous voulons définir la date limite d'attente du thread, puis nous pouvons le faire par l'interface Condition.
Dans certaines situations, nous ne voulons pas que le thread soit interrompu et que le thread actuel attende jusqu'à ce qu'il soit signalé, alors nous pouvons opter pour la méthode waitUninterruptably présente dans l'interface de condition.
Pour plus d'informations Condition Interface Java Documentation:
Le plus gros problème est que l'attente/notification est sujette aux erreurs pour les nouveaux développeurs. Le problème principal est de ne pas savoir comment les gérer correctement peut entraîner un bug obscur.
Condition englobe cette fonctionnalité dans un composant dédié, mais elle se comporte à peu près de la même manière.
Il y a une question concernant wait/nofity posté quelques minutes avant celui-ci et beaucoup, beaucoup plus Rechercher [Java] + wait + notify
Lorsque vous utilisez Condition: await()/signal()
, vous pouvez distinguer quel objet ou groupe d'objets/threads reçoit un signal spécifique. Voici un court exemple où certains threads, les producteurs, recevront le signal isEmpty
tandis que les consommateurs recevront le signal isFull
:
private volatile boolean usedData = true;//mutex for data
private final Lock lock = new ReentrantLock();
private final Condition isEmpty = lock.newCondition();
private final Condition isFull = lock.newCondition();
public void setData(int data) throws InterruptedException {
lock.lock();
try {
while(!usedData) {//wait for data to be used
isEmpty.await();
}
this.data = data;
isFull.signal();//broadcast that the data is now full.
usedData = false;//tell others I created new data.
}finally {
lock.unlock();//interrupt or not, release lock
}
}
public void getData() throws InterruptedException{
lock.lock();
try {
while(usedData) {//usedData is lingo for empty
isFull.await();
}
isEmpty.signal();//tell the producers to produce some more.
usedData = true;//tell others I have used the data.
}finally {//interrupted or not, always release lock
lock.unlock();
}
}
@AfterWorkGuinness
Ne devez-vous pas définir usedData = true/false avant de signaler
Après le code du signal, allez jusqu'à la fin du bloc de verrouillage et libérez-le, donc l'ordre n'a pas d'importance
Pour expliquer précisément pourquoi avoir plusieurs ensembles d'attente est un avantage:
Avec wait/notify s'il y a différentes choses que les threads attendent (l'exemple courant est une file d'attente de blocage de taille fixe, avec certains threads mettant des choses dans la file d'attente et bloquant lorsque la file d'attente est pleine, et d'autres threads prenant de la file d'attente et bloquant lorsque la file d'attente est vide), puis si vous utilisez notifier, ce qui oblige le planificateur à choisir un thread de l'ensemble d'attente pour notifier, vous pouvez avoir des cas de coin où le thread choisi n'est pas intéressé à être notifié pour une situation particulière. Par exemple, la file d'attente notifiera l'ajout de quelque chose à la file d'attente, mais si le thread choisi est un producteur et que la file d'attente est pleine, elle ne peut pas agir sur cette notification, que vous auriez préféré aller voir un consommateur. Avec le verrouillage intrinsèque, vous devez utiliser notifyAll afin de vous assurer que les notifications ne se perdent pas.
Mais notifyAll entraîne un désabonnement à chaque appel, où chaque thread se réveille et se bat pour le verrou, mais un seul peut progresser. Les autres threads se bousculent tous en se disputant le verrou jusqu'à ce qu'ils puissent, un à la fois, acquérir le verrou et retourner à l'attente. Cela génère beaucoup de conflits pour peu d'avantages, il serait préférable de pouvoir utiliser notifier et savoir qu'un seul thread est notifié, lorsque la notification est pertinente pour ce thread.
C'est là qu'avoir des conditions distinctes à attendre est une grande amélioration. La file d'attente peut appeler un signal sur une condition et savoir qu'elle ne réveillera qu'un seul thread, où ce thread attend spécifiquement la condition.
Doc API pour Condition a un exemple de code qui montre l'utilisation de plusieurs conditions pour un tampon borné, il dit:
Nous aimerions continuer à attendre de mettre des threads et à prendre des threads dans des ensembles d'attente distincts afin de pouvoir utiliser l'optimisation de la notification d'un seul thread à la fois lorsque des éléments ou des espaces deviennent disponibles dans le tampon.