47 votes

Comprendre le code de la méthode de calcul ConcurrentHashMap

Je viens de trouver ce code étrange dans la méthode de calcul ConcurrentHashMap: (ligne 1847)

 public V compute(K key,
                 BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
    ...
    Node<K,V> r = new ReservationNode<K,V>();
    synchronized (r) {   <--- what is this?
        if (casTabAt(tab, i, null, r)) {
            binCount = 1;
            Node<K,V> node = null;
 

Le code effectue donc la synchronisation sur une nouvelle variable disponible uniquement pour le thread actuel. Cela signifie qu'il n'y a pas d'autre thread en concurrence pour ce verrou ou pour provoquer des effets de mémoire cache.

Quel est le but de cette action? Est-ce une erreur ou des effets secondaires non évidents dont je ne suis pas au courant?

ps jdk1.8.0_131

40voto

Andy Turner Points 13883
casTabAt(tab, i, null, r)

la publication, par la référence à l' r.

static final <K,V> boolean casTabAt(Node<K,V>[] tab, int i,
                                    Node<K,V> c, Node<K,V> v) {
    return U.compareAndSwapObject(tab, ((long)i << ASHIFT) + ABASE, c, v);
}

Parce qu' c en tab, il est possible que l'on y accède par un autre thread, par exemple, en putVal. En tant que tel, ce synchronized bloc est nécessaire d'exclure les autres threads de faire d'autres synchronisé choses avec qu' Node.

16voto

Kayaman Points 12541

Alors qu' r est une nouvelle variable à ce point, il est mis à l'interne table immédiatement par if (casTabAt(tab, i, null, r)) , à quel point un autre thread est en mesure d'y accéder dans les différentes parties du code.

Interne non-javadoc commentaire le décrit ainsi

D'Insertion (via le mettre ou de ses variantes) du premier noeud dans un bac vide est effectuée par juste Boîtier à la poubelle. C'est de loin le plus cas le plus courant pour les opérations en vertu de la plupart des principales/hachage distributions. D'autres opérations de mise à jour (insert, delete, et de le remplacer) requièrent des verrous. Nous ne voulez pas gaspiller de l'espace requis pour associer un distinctes verrouillage de l'objet avec chaque cellule, de sorte qu'au lieu d'utiliser le premier nœud de bin liste lui-même comme un verrou. Verrouillage de soutien pour ces verrous s'appuie sur builtin "synchronisé" moniteurs.

5voto

Eugene Points 6271

Juste de 0,02$ ici

Ce que vous avez montré qu'il est en fait juste l' ReservationNode - ce qui signifie que le bac est vide et qu'une réserve de certaines Nœud est fait. Notez que cette méthode tard remplace ce Nœud avec un réel :

 setTabAt(tab, i, node);

Si cela est fait dans le but de le remplacer atomique comme je le comprends. Une fois publié, via casTabAt et si d'autres threads de le voir - qu'ils ne peuvent pas synchroniser depuis la serrure est déjà jugé.

Notez également que lorsqu'il y a une Entrée dans une poubelle, qui est le premier Nœud est utilisé pour synchroniser (c'est plus bas dans la méthode):

boolean added = false;
            synchronized (f) { // locks the bin on the first Node
                if (tabAt(tab, i) == f) {
......

En tant que nœud, cette méthode a changé en 9, depuis 8. Par exemple, l'exécution de ce code:

 map.computeIfAbsent("KEY", s -> {
    map.computeIfAbsent("KEY"), s -> {
        return 2;
    }
 })

ne serait jamais arrivée à 8, mais jeter un Recursive Update en 9.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X