J'ai la classe suivante qui contient un seul champ Comment puis-je mettre en œuvre des égaux () Pour une classe qui a des champs synchronisés? p> i code>. L'accès à ce champ est gardé par la serrure de l'objet ("this"). Lors de la mise en œuvre égale (), j'ai besoin de verrouiller cette instance (a) et l'autre (b). Si thread 1 appelle A.Equals (b) et au même moment fileter 2 appels B.Qui (a), l'ordre de verrouillage est inverse dans les deux implémentations et peut entraîner une impasse.
14 Réponses :
La bonne implémentation de est égale () code> et
hashcode () code> est requis par diverses choses telles que les structures de données de hachage, et donc vous n'avez donc aucune option réelle. D'une autre perspective,
égale () code> et
hashcode () code> ne sont que des méthodes, avec les mêmes exigences sur la synchronisation que d'autres méthodes. Vous avez toujours le problème de l'impasse, mais ce n'est pas spécifique au fait que
est égal () code> qui le cause. p>
Pourquoi synchroniser? Quel est le cas d'utilisation où il est important que cela puisse changer pendant la comparaison et que cela n'a pas d'importance si si elle change immédiatement après avant le code en fonction des courses d'égalité. (c.-à-d. Si vous avez du code en fonction de l'égalité, que se passe-t-il si les valeurs deviennent inégales avant ou pendant ce code) p>
Je pense que vous devez jeter un coup d'œil au processus plus large pour voir où vous devez verrouiller. P>
Vous êtes correct qu'une valeur pourrait changer immédiatement après la comparaison, mais lorsque la valeur enveloppée est plus intéressante qu'une primitive int code>, il existe une possibilité de cette valeur dans un état incohérent, sauf si elle est synchronisée pendant que ses parties sont examinés.
Vrai mais vous avez toujours le problème de quoi faire après les égaux
En fait, la question est une visibilité. Sans utiliser synchronisé, un ordre valide pourrait toujours entraîner un résultat inattendu (c'est-à-dire une valeur définie plus tôt, n'est pas visible quand elle devrait être).
Toujours les enfermer dans le même ordre, une façon dont vous pouvez décider de la commande est sur les résultats de système.identityhashcode (objet) code>
p>
Modifier pour inclure Commentaire: em> p>
La meilleure solution pour traiter le cas rare des codes identitaires étant égaux nécessite plus de détails sur ce que l'autre verrouillage de ces objets se passe. P>
Tous les exigences de verrouillage de plusieurs objets multiples doivent utiliser le même processus de résolution. P>
Vous pouvez créer un utilitaire partagé pour suivre des objets avec le même IdentityHashCode pour la courte période des exigences de verrouillage et fournir une commande répétable pour eux pour la période de suivie. P>
Boolean public est égal (objet obj) {si (this == obj) retourne vrai; Si (obj == null) renvoie false; Si (getclass ()! = obj.getclass ()) renvoie false; Synchroniser autre = (synchroniser) obj; boolean plus petithash = système.ididentityhashcode (this)
essayer de synchroniser Si les objets sont autorisés à muter de manière à modifier les résultats de Le égale code> et
hashcode code> à l'intérieur de l'objet ne fonctionnera pas correctement. Considérez le cas d'un
hashmap code> qui utilise
hashcode code> pour découvrir quel objet "godet" un objet sera dans, puis utilise
égale code> sur la recherche de manière séquentielle objets dans le seau. p>
hashcode code> ou
égal code> vous pouvez vous retrouver avec un scénario où
hashmap code> Appels
hashcode code>. Il acquiert la serrure, obtient le hachage et libère la serrure.
hashmap code> puis procède pour calculer le "godet" à utiliser. Mais avant
hashmap code> peut acquérir le verrouillage sur une autre personne d'autre attrape la serrure et la mutation de l'objet de sorte que
est égal à code> devenir incompatible avec la valeur précédente de
hashcode code> . Cela conduira à des résultats catastrophiques. P>
hashcode code> et est utilisé dans de nombreux endroits et est noyau de l'API de collections Java. J'aurais peut-être une valeur précieuse pour repenser votre structure d'application qui ne nécessite pas d'accès synchronisé à ces méthodes. Ou à tout le moins ne se synchronisez pas sur l'objet lui-même. p>
Pourquoi le bowvote ?? Si vous regardez les cours du JDK (java.util.date ou java.lang.long), vous constaterez qu'ils ne sont pas synchronisés non plus.
"Si des objets sont autorisés à muter de manière à modifier les résultats de HashCode ou égal à ..." La plupart des objets font - même Java.Util.date (Setttime (long)). Il est toujours nécessaire de garder cela à l'esprit lors de l'utilisation d'une structure de données hachée - ne le change pas après l'avoir déposé. Pourtant, il est permis de le changer.
Où est le point dans la synchronisation des égaux () si le résultat n'est pas garanti d'être vrai après la synchronisation après la synchronisation:
synchronized(o1) { synchronized(o2) { if (o1.equals(o2)) { // is o1 still equal to o2? } } }
Le seul moyen de savoir si la synchronisation est strictement nécessaire consiste à analyser l'ensemble du programme pour les situations. Il y a deux choses que vous devez rechercher; Situations où un thread change un objet tandis qu'un autre appelle des égaux et des situations où le fil appelant Si vous verrouillez les deux ceci ne garantit pas que les deux objets ont la même valeur de < Code> I CODE> en même temps, mais il est peu probable de faire une différence pratique. Après tout, même si vous avez eu cette garantie, vous devez toujours faire face à la question que les deux objets ne peuvent plus être égaux au moment où le En outre, cela n'élimine pas entièrement le risque d'impasse. Considérez le cas où un thread peut appeler maintenant si deux threads appellent (Cependant, vous devez appeler Ceci a été Dit, il y a quelque chose de plus préoccupé par une méthode est égal à code> peut voir une valeur fade de
i code>.
Ce code> et le
objet code> en même temps que vous risquez effectivement une impasse. Mais je poserais une question à ce que vous devez faire cela. Au lieu de cela, je pense que vous devriez implémenter
égaux (objet) code> comme ceci: p>
est égal à code> renvoyé. (Ceci est le point de vue @ S!) P>
égale code> tout en maintenant une serrure sur l'un des deux objets; par exemple p>
a.frobbitt (b) code> et
b.frobbitt (a) code> respectivement, Il y a un risque d'impasse. p>
geti () code> ou déclarer
i code> pour être
volatile code>, sinon le
égale () code> pourrait voir une valeur fade de
i code> s'il a été récemment mis à jour par un fil différent.) P>
basée sur une valeur ajoutée code> sur un objet dont les valeurs des composants peuvent être mutées. Par exemple, cela va briser de nombreux types de collecte. Combinez cela avec un multi-threading et vous allez avoir beaucoup de difficulté à déterminer si votre code est vraiment correct. Je ne peux pas m'empêcher de penser que vous feriez mieux de changer le
Equals code> et
HashCode code> de sorte qu'ils ne dépendent pas d'état pouvant muté après les méthodes qui ont été appelées le premier temps. p> p>
> En outre, cela n'élimine pas entièrement le risque d'impasse. Considérez le cas où un thread peut appeler des égaux tout en maintenant une serrure sur l'un des deux objets. Je ne sais pas comment c'est un problème. Si c'est la serrure de cela, aucun problème comme Geti () ne sera réentrant et n'influencera pas d'autres.Getti (). Même chose si c'est la serrure de l'autre. Juste pour référence sur la façon dont d'autres le font: Vector se synchronise à ce sujet mais pas de l'autre dans des égaux (). Cela peut entraîner une exception simultané une exception si le vecteur est modifié tandis que c'est égal () fonctionne. Atomicinteger ne met pas en œuvre égaux ()
@Philipp - Vous auriez besoin de deux threads en faisant cela avec la même paire d'objets dans des positions opposées pour obtenir une impasse.
Cela ne sera-t-il pas appelé son verrou sur l'autre.Getti () est appelé? Je ne comprends pas comment cela pourrait être une impasse.
this.geti () code> n'acquit ni ne libère pas le verrou, car
frobbitt () code> l'a déjà acquis avant appeler
ceci.geti () code>. C'est la même situation que dans votre question, mais avec une méthode différente. Deux threads appelant
a.froiditt (b) code> et
b.froiditt (a) code> en même temps acquiert la verrouillage de
A code> resp.
B code>, puis attendez le verrouillage de
B code> resp.
A code> pour appeler
autre.geti () code>, entraînant une impasse.
(Je suppose que vous êtes intéressé par l'affaire général ici, et pas seulement dans des entiers emballés.) p>
Vous ne pouvez pas empêcher deux threads d'appeler Par conséquent, la synchronisation protégerait contre le cas de la valeur enveloppée étant dans un état incohérent alors que vous essayez de faire le comparateur (par exemple, deux définir code> ... méthodes dans l'ordre arbitraire. Donc, même lorsqu'un thread obtient un (valide)
true code> de l'appelant
.fequals ( code> ...
) code>, ce résultat pourrait être invalidé immédiatement par un autre Fil que cela appelle
définir code> ... sur l'un des objets. Iow le résultat signifie que les valeurs étaient égales à l'instant de comparaison. P>
int code> -sized moitiés d'un
long < / code> être mis à jour consécutivement). Vous pouvez éviter une condition de course en copiant chaque valeur (c'est-à-dire synchronisée indépendamment, sans chevauchement), puis comparant les copies. P>
lit et écrit à De même, vous n'avez pas besoin de synchroniser int code> est déjà atomique, il n'est donc pas nécessaire de synchroniser le getter et le setter (voir
égale code> ici. Bien que vous puissiez empêcher un autre thread de changer l'une des valeurs
i code> pendant la comparaison, ce fil bloquait simplement jusqu'à ce que la méthode code> est terminée et la modifie immédiatement après. P>
Comme le montre la journée de Jason, l'entier se compare déjà atomique, la synchronisation est donc superflue. Mais si vous construisiez simplement un exemple simplifié et dans la vie réelle, vous envisagez d'un objet plus complexe:
La réponse directe à votre question est, assurez-vous de comparer toujours les éléments dans un ordre cohérent. Peu importe ce que cet ordre est, tant que c'est cohérent. Dans un cas comme celui-ci, System.IDidIfidHashashCode fournirait une commande, comme: p> puis déclarer à l'égalshelper privé et laissez le vrai travail de comparaison. P> < P> (mais WOW, c'est beaucoup de code pour une question aussi triviale.) p> Notez que pour que cela fonctionne, toute fonction pouvant modifier l'état de l'objet devrait être déclarée synchronisée. < / p> Une autre option serait de synchroniser Sync.Class plutôt que sur l'un ou l'autre objet, puis de synchroniser toutes les configurissures sur Sync.Class. Cela ferait tout verrouiller sur un seul mutex et éviterait tout le problème. Bien sûr, en fonction de ce que vous faites cela pourrait entraîner un blocage indésirable de certains threads. Vous devez réfléchir grâce aux implications à la lumière de ce que votre programme est à propos. P> S'il s'agit d'un vrai problème dans un projet que vous travaillez, une alternative sérieuse à envisager serait de faire le objet immuable. Pensez à la chaîne et à StringBuilder. Vous pouvez créer un objet SyncBuilder qui vous permet de faire du travail que vous devez créer une de ces choses, puis disposez d'un objet de synchronisation dont l'état est défini par le constructeur et ne peut jamais changer. Créez un constructeur qui prend un syncbuilder et définit son état pour correspondre ou avoir une méthode SyncBuilder.tosync. De toute façon, vous faites tout votre bâtiment dans SyncBuilder, puis transformez-le en une synchronisation et vous êtes maintenant une garantie d'immuabilité afin que vous n'ayez pas à vous gâcher avec la synchronisation du tout. P> P>
S'il a été dit assez, les champs que vous utilisez pour HASHCODE (), sont égaux () ou comparète () doivent être immuables, de préférence définitifs. Dans ce cas, vous n'avez pas besoin de les synchroniser. P>
La seule raison d'implémenter HashCode () est donc l'objet peut être ajouté à une collection de hachage et vous ne pouvez pas valablement modifier le hashcode () d'un objet qui a été ajouté à une telle collection. P>
Comme les calculs pour l'arraylist? ;-) Joking de côté, la finale immuable est une bonne chose à rechercher (probablement au-delà d'une bonne chose - tout est SO I> beaucoup plus facile lorsque vous utilisez des objets immuables), mais ce n'est pas toujours possible avec de grands objets composites .
Comment utilisez-vous HashCode () avec ArrayList? Bien qu'il ne soit peut-être pas possible de rendre les objets immuables, il ne change pas le fait que vous ne pouvez pas modifier le hashcode () d'une clé d'une carte ou d'éléments d'un ensemble et qu'il fonctionne.
Ne pas utiliser de synchronisation. Pensez à des haricots non modifiables. P>
Vous essayez de définir un "égal" basé sur le contenu et "hashcode" sur un objet mutable. Ceci n'est pas seulement impossible: cela n'a pas de sens. Selon P>
http://java.sun.com.com /javase/6/docs/api/java/lang/Object.html P>
"Equals" et "HashCode" doivent être cohérents: renvoyez la même valeur pour les invocations successives sur le même objet. Mutabilité par définition empêche cela. Ce n'est pas seulement la théorie: de nombreuses autres classes (par exemple, des collections) dépendent des objets mettant en œuvre la sémantique correcte pour les égaux / HashCode. P>
Le problème de synchronisation est un hareng rouge ici. Lorsque vous résolvez le problème sous-jacent (mutabilité), vous n'avez pas besoin de synchroniser. Si vous ne résolvez pas le problème de l'entretabilité, aucune synchronisation ne vous aidera. P>
Les objets mutables de la JVM effectuent des égaux (Exemple: ArrayList, HASHMAP) Donc, votre affirmation selon laquelle "Contenu" "est égal à" (...) sur un objet mutable (...) n'a pas de sens "n'est pas partagé par Ceux qui écrivent la JVM. Même certaines classes mutables sychronisées implémentent des égaux (exemple vectoriel, qui n'est pas une impasse Safe BTW)!
C'est un bon point. Cependant, je dirais que c'est la responsabilité de l'appelant de rendre ces objets efficacement immuables pour la durée de toute opération qui utilise des égaux / HashCode. Par exemple, la liste d'appel
Vous devez vous assurer que les objets ne changent pas entre les appels vers HASHCODE () et sont égaux () (si appelé). Ensuite, vous devez vous assurer que les objets ne changent pas (à l'étendue que les hachices et les égaux sont concernés), tandis que l'objet est assis dans un hachema. Pour changer l'objet, vous devez d'abord le supprimer, puis le changer et le remettre. P>
Comme d'autres personnes ont mentionné, si les choses changent pendant la vérification des égaux, il existe déjà une possibilité de comportement fou (même avec une synchronisation correcte). Ainsi, tout ce dont vous avez vraiment besoin de vous inquiéter est la visibilité (vous voulez vous assurer de changer de changement qui "se passe avant" votre appel égal à votre équivalence est visible). Par conséquent, vous pouvez simplement faire "instantané" égaux, ce qui sera correct en termes de relation "arrive avant" et ne souffrira pas de problèmes de commande de verrouillage: