À quoi servent les clôtures de mémoire en Java?
Tout en essayant de comprendre comment SubmissionPublisher
(code source en Java SE 10, OpenJDK | docs), une nouvelle classe ajoutée à Java SE dans la version 9, a été implémentée, je suis tombé sur quelques appels d'API pour VarHandle
Je n'étais pas au courant auparavant de:
fullFence
, acquireFence
, releaseFence
, loadLoadFence
et storeStoreFence
.
Après avoir fait quelques recherches, en particulier concernant le concept de barrières/clôtures de mémoire (j'en ai déjà entendu parler, oui; mais je ne les ai jamais utilisées, donc était assez peu familier avec leur sémantique), je pense que j'ai une compréhension de base de ce qu'ils sont pour. Néanmoins, comme mes questions peuvent provenir d'une idée fausse, je veux m'assurer que j'ai bien compris en premier lieu:
Les barrières de mémoire sont des contraintes de réorganisation concernant les opérations de lecture et d'écriture.
Les barrières de mémoire peuvent être classées en deux catégories principales: les barrières de mémoire unidirectionnelles et bidirectionnelles, selon que ils définissent des contraintes sur les lectures ou les écritures, ou les deux.
-
C++ prend en charge une variété de barrières de mémoire, cependant, celles-ci ne correspondent pas à celles fournies par
VarHandle
. Cependant, certaines des barrières de mémoire disponibles dansVarHandle
fournissent des effets de commandequi sont compatibles avec leurs barrières de mémoire C++ correspondantes.-
{[9] } est compatible avec
atomic_thread_fence(memory_order_seq_cst)
-
{[11] } est compatible avec
atomic_thread_fence(memory_order_acquire)
-
#releaseFence
est compatible avecatomic_thread_fence(memory_order_release)
-
#loadLoadFence
et#storeStoreFence
n'ont pas de contre-partie C++ compatible
-
{[9] } est compatible avec
Le mot compatible semble vraiment important ici car la sémantique diffère clairement en ce qui concerne les détails. Par exemple, toutes les barrières C++ sont bidirectionnelles, alors que les barrières Java ne le sont pas (nécessairement).
Mes hypothèses sont-elles correctes? Si oui, mes questions résultantes sont:
Faites les barrières de mémoire disponibles dans
VarHandle
cause tout type de synchronisation de la mémoire?Qu'elles provoquent ou non la synchronisation de la mémoire, à quoi peuvent servir les contraintes de réorganisation en Java? Le modèle de mémoire Java donne déjà des garanties très fortes concernant la commande lorsque des champs volatils, des verrous ou des opérations
VarHandle
comme#compareAndSet
sont impliqués.
Dans le cas où vous cherchez un exemple: Le BufferedSubscription
susmentionné, une classe interne de SubmissionPublisher
(source liée ci-dessus), a établi un clôture dans la ligne 1079 (function growAndAdd
; comme le site Web lié ne prend pas en charge les identifiants de fragment, il suffit de CTRL+F pour cela). Cependant, il n'est pas clair pour moi à quoi il sert.
1 answers
C'est surtout une non-réponse, vraiment (au départ je voulais faire un commentaire, mais comme vous pouvez le voir, c'est beaucoup trop long). C'est juste que j'ai interrogé moi-même beaucoup, fait beaucoup de lecture et de recherche et à ce moment, je peux dire: c'est compliqué. J'ai même écrit plusieurs tests avec jcstress pour comprendre comment ils fonctionnent vraiment (tout en regardant le code assembleur généré) et alors que certains d'entre eux en quelque sorte avaient du sens, le sujet en général n'est en aucun cas facile.
, La première chose que vous devez comprendre:
La spécification du langage Java (JLS) ne mentionne pas les barrières , n'importe où. Ceci, pour java, serait un détail d'implémentation: il agit vraiment en termes de se produit avant sémantique. Pour pouvoir les spécifier correctement en fonction du JMM (Modèle de mémoire Java), le JMM devrait changer beaucoup.
Il s'agit de travaux en cours.
Deuxièmement, si vous je veux vraiment gratter la surface ici, c'est la toute première chose à regarder . Le discours est incroyable. Ma partie préférée est quand Herb Sutter lève ses 5 doigts et dit: "C'est combien de personnes peuvent vraiment et correctement travailler avec ceux-ci."Cela devrait vous donner un indice de la complexité. Néanmoins, il existe des exemples triviaux faciles à saisir (comme un compteur mis à jour par plusieurs threads qui ne se soucie pas des autres garanties de mémoire, mais se soucie seulement qu'il est lui-même incrémenté correctement).
Un autre exemple est quand (en java) vous voulez un indicateur volatile
pour contrôler les threads pour arrêter/démarrer. Vous savez, le classique:
volatile boolean stop = false; // on thread writes, one thread reads this
Si vous travaillez avec java, vous savez que sans volatile
ce code est cassé (vous pouvez lire pourquoi doubler vérifier le verrouillage est cassé, sans elle, par exemple). Mais savez-vous aussi que pour certaines personnes qui écrivent du code haute performance, c'est trop? volatile
lecture/écriture garantit également séquentiel cohérence - cela a de fortes garanties et certaines personnes veulent une version plus faible de cela.
Un drapeau thread safe, mais pas volatile? Oui, exactement:
VarHandle::set/getOpaque
.
Et vous vous demanderiez pourquoi quelqu'un pourrait avoir besoin de cela par exemple? Tout le monde n'est pas intéressé par tous les changements qui sont soutenus par un volatile
.
Voyons comment nous allons y parvenir en java. Tout d'abord, de telles choses exotiques existaient déjà dans l'API: AtomicInteger::lazySet
. Ceci n'est pas spécifié dans le modèle de mémoire Java et n'a pas de définition claire; encore des gens l'ont utilisé (LMAX, afaik ou ceci pour plus de lecture). À mon humble avis, AtomicInteger::lazySet
est VarHandle::releaseFence
(ou VarHandle::storeStoreFence
).
Nous allons essayer de répondre pourquoi quelqu'un a besoin de ces?
JMM a essentiellement deux façons d'accéder à un champ: plaine et volatils (ce qui garantit séquentielle cohérence). Toutes ces méthodes que vous mentionnez sont là pour apporter quelque chose entre ces deux - libérer/acquérir la sémantique; il y a des cas, je suppose, où les gens en fait en ont besoin.
Un même plus relaxation à partir de presse/acquérir est opaque, qui je suis encore à essayer de comprendre pleinement les.
Donc bottom line (votre compréhension est assez correcte, btw): si vous prévoyez de l'utiliser en java - ils n'ont pas de spécification pour le moment, faites-le à vos risques et périls. Si vous ne voulez pour les comprendre, leurs modes équivalents C++ sont le point de départ.