En entrant dans synchronisé bloc atomique?
savez-vous s'il est garanti que le bloc synchronisé en java est atomique?
imaginez le cas suivant
Thread_1, 2:
synchronized(object){object.modify();}
(l'objet est une variable partagée.)
imaginez thread_M va changer la référence à un objet comme
synchronized(object){object = new Object()}
imaginez maintenant que les threads 1 et 2 sont en compétition pour obtenir le verrouillage sur l'objet
est-il possible que
1. Thread1: lire ancien objet
2. ThreadM: modifier la référence d'objet et la libération de l'ancien objet de verrouillage
3. Thread2: lire un nouvel objet; vérifier le verrouillage; le verrouiller
4. Thread1: vérification de verrouillage (ok cos ancien objet a été lu); verrouiller sur elle
maintenant les deux threads ont une serrure et modifier le même objet (nouveau)
donc pour préciser ma question - Est quelque part garantie que dans les étapes(objet) synchronisées (1 et 4) sont atomiques (comme décrit à l'étape 3)?
3 réponses
vous can réattribuez object
pendant que vous êtes synchronisé sur object
, mais je ne peux pas penser à un scénario où la réattribution d'un champ utilisé pour le verrouillage est une bonne idée.
aucun autre thread ne pourra acquérir la serrure sur l'ancienne valeur de object
jusqu'à ce que thread m sorte de son bloc synchronisé, mais un autre thread pourra acquérir une serrure sur le nouvel objet dès qu'il sera visible à ce thread.
Les Modifications faites par un thread avant de libérer une serrure sont garanties pour être visibles aux threads qui acquièrent la serrure après. Mais puisque vous réassignez la serrure elle-même, le thread d'acquisition peut ne pas voir qu'elle a été changée, et acquérir une serrure sur l'ancienne valeur. Alors ils ne verraient toujours pas que object
a été réaffecté.
déclarer object
comme variable volatile
permettrait de s'assurer que sa valeur "courante" est utilisée pour le verrouillage. Mais il ne serait pas empêcher deux threads de modifier la même instance simultanément:
- le fil M se verrouille sur l'ancienne valeur. Thread 1 lit l'ancienne valeur.
- Thread m change la valeur.
- Thread m déverrouille l'ancienne valeur. Thread 2 lit la nouvelle valeur.
- Thread 1 acquiert un verrou sur l'ancienne valeur. Thread 2 acquiert une nouvelle valeur.
- Thread 1 lit new value. Fil 2 lit nouvelle valeur.
- Thread 1 modifie la nouvelle valeur. Thread 2 modifie une nouvelle valeur.
pour éviter tout cela, il suffit de créer un objet séparé pour le verrouillage, et ne jamais le changer.
supposons que vous ayez une variable, foo
:
Foo foo;
Et supposons qu'il contient une référence à un objet:
foo = new Foo(...);
et supposons que nous ayons un bloc synchronized
:
synchronized(foo) {
...
}
le synchronized
keywoord ne pas fonctionne sur la variable, foo
, et il ne pas fonctionne sur les déclarations dans le bloc synchronisé.
la seule chose que le mot-clé synchronized
fait ici est d'empêcher les autres threads de se synchroniser sur la même instance en même temps.
si vous réassignez la variable, foo
pour faire référence à une instance différente alors que le thread A est à l'intérieur du bloc, alors un autre thread B pourra entrer dans le même bloc en même temps parce que chacun des deux threads sera synchronisé sur une instance différente.
vous êtes en train de synchroniser l'objet vers lequel" l'objet " pointe, et non la variable contenant la valeur.
cependant, parce que les deux morceaux de code se synchronisent sur votre objet avant d'aller de l'avant, vous êtes en sécurité -- bien que ce soit un mauvais modèle de conception.
vous pouvez trouver moins de confusion si vous utilisez des méthodes synchronisées au lieu de blocs de code synchronisés.
aussi, à mon avis, mais synchronisé (objet) semble comme un Mauvaise conception de modèle. C'est juste mon opinion, mais je ne fais jamais quelque chose comme ça.