Java 奇怪的HashMap异常(HashMap$节点不能强制转换为HashMap$TreeNode)

Java 奇怪的HashMap异常(HashMap$节点不能强制转换为HashMap$TreeNode),java,hashmap,Java,Hashmap,在另一个关于stackoverflow的问题被问到之后, () 我开始试验HashMap。以下是我编写的几行代码: import java.util.HashMap; import java.util.Random; public class Concurrency { public static void putEntriesToMap(HashMap<String, String> hashMap) { for (int count = 0; count

在另一个关于stackoverflow的问题被问到之后, () 我开始试验HashMap。以下是我编写的几行代码:

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();
    }
}
然而,这对我来说似乎很奇怪,因为它看起来像是一个内部HashMap错误。 我知道并发性没有正确使用,但它是故意使用的

我试图用谷歌搜索这个例外,但几乎找不到任何信息

你能重现同样的异常吗

我正在使用oracle jdk 1.8.0\u 40

编辑:

首先,谢谢你的回答,我现在明白了。 我只想指出,我知道如何通过使用线程安全预防措施来避免程序崩溃,但我不知道为什么在给定情况下会抛出此异常。
托马斯在下面的评论中解释得很好。这在公认的答案中也得到了很好的解释。再次感谢:)。

我在您的代码中也发现了相同的异常。我在
putenriestomap()
方法上添加了一个
synchronized
修饰符,错误似乎停止了。问题是两个线程同时修改同一个映射。必须转换一个对象才能将条目放入。然而,第二个线程正在处理一个变异对象,它抛出一个
ClassCastException
。因此,请确保没有两个线程同时访问同一个映射。
synchronized
修饰符阻止所有其他线程对类/实例执行任何操作,如果其他线程正在执行相同的操作。同步的静态方法同步类本身,而同步的非静态方法只同步类的实例。

我得到了相同的
ClassCastException
,同时调用。我通过更改使用的实现进行了修复。

这只是“并发未正确使用”的结果--
HashMap
需要外部同步才能保证线程安全。这是可能的,当您不在不同线程之间同步时,这里的问题是什么?使用并发包中的集合:问题是,为什么这个例外如此奇怪。在查看HashMap代码之后,似乎节点对象是在TreeNode上铸造的,而不是TreeNode实例。这就是抛出异常的原因。但是,为什么只有当并发被错误地使用时才会发生这种情况呢?正如您所看到的,Java8的HashMap有一个名为
treeify
的方法来改进内部存储。由于您没有以线程安全的方式使用映射(正如其他线程已经评论的那样),一个线程依赖于类
TreeNode
的条目,而另一个线程很可能将相同的引用更改为类
Node
的条目(两者都扩展
map.entry
)。
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)