web-dev-qa-db-fra.com

Strange HashMap exception (HashMap $ Node ne peut pas être converti en HashMap $ TreeNode)

Après une autre question posée sur stackoverflow, .__ ( Java - Pourquoi ce programme ne lève pas d’exception de modification simultanée ) J’ai commencé à expérimenter avec HashMap. Voici quelques lignes de code que j'ai écrites:

import Java.util.HashMap;
import Java.util.Random;

public class Concurrency {
    public static void putEntriesToMap(HashMap<String, String> hashMap) {
        for (int count = 0; count < 10000; count++) {
            hashMap.put(Integer.toString(count), Integer.toString(count));
            Random random = new Random();
            if (random.nextBoolean()) {
                hashMap.remove(count + "");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        final HashMap<String, String> hashMap = new HashMap<String, String>();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                putEntriesToMap(hashMap);
            }
        });

        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                putEntriesToMap(hashMap);
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
    }
}

Il était une fois (environ 1 course sur 20), en exécutant ce code, je reçois

Exception in thread "Thread-0" Exception in thread "Thread-1" Java.lang.ClassCastException: Java.util.HashMap$Node cannot be cast to Java.util.HashMap$TreeNode
    at Java.util.HashMap$TreeNode.moveRootToFront(HashMap.Java:1819)
    at Java.util.HashMap$TreeNode.treeify(HashMap.Java:1936)
    at Java.util.HashMap.treeifyBin(HashMap.Java:771)
    at Java.util.HashMap.putVal(HashMap.Java:643)
    at Java.util.HashMap.put(HashMap.Java:611)
    at Concurrency.putEntriesToMap(Concurrency.Java:9)
    at Concurrency$1.run(Concurrency.Java:27)
    at Java.lang.Thread.run(Thread.Java:745)

Cela me semble toutefois étrange, car il semble que ce soit une erreur interne HashMap. Je sais que la simultanéité n’est pas utilisée correctement, mais c’est fait à dessein.

J'ai essayé de google l'exception, mais je n'ai trouvé presque aucune information.

Pouvez-vous même reproduire la même exception?

J'utilise Oracle jdk 1.8.0_40

MODIFIER:

Tout d’abord, merci pour les réponses, c’est maintenant clair pour moi ..__ Je tiens à souligner que je savais comment éviter le programme en utilisant des précautions de thread thread, mais je ne savais pas pourquoi cette exception était levée. dans une situation donnée. Thomas l’a très bien expliqué dans les commentaires ci-dessous. Cela est également bien expliqué dans la réponse acceptée. Merci encore :).

27
pnadczuk

J'ai aussi trouvé la même exception avec votre code. J'ai ajouté un modificateur synchronized à la méthode putEntriesToMap() et l'erreur semble ne plus se produire. Le problème est que les deux threads modifient la même carte en même temps. Il existe un objet qui doit être converti pour que l'entrée soit insérée. Cependant, le second thread traite d'un objet muté, qui jette une ClassCastException. Donc, assurez-vous que deux threads n’ont pas accès à la même carte en même temps. Le modificateur synchronized empêche tous les autres threads de faire quoi que ce soit avec la classe/instance si un autre thread fait de même. Les méthodes statiques synchronisées synchronisent la classe elle-même, alors que les méthodes synchronisées non statiques ne synchronisent que l'instance de la classe.

16
HyperNeutrino

Je recevais la même ClassCastException avec des appels simultanés à HashMap.computeIfAbsent . J'ai corrigé en modifiant l'implémentation pour utiliser ConcurrentHashMap .

2
Clay