奇怪的HashMap异常(HashMap$Node不能被强制转换为HashMap$TreeNode)

2022-09-01 01:39:20

在堆栈溢出上提出的另一个问题(Java-为什么这个程序不抛出并发修改异常)之后,我开始尝试使用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();
    }
}

曾几何时(大约20次运行1次),在执行此代码时,我得到

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)

然而,这对我来说似乎很奇怪,因为它看起来像是一个内部的HashMap错误。我知道并发没有被正确使用,但它是故意的。

我试图谷歌例外,但我几乎没有找到任何信息。

您甚至可以重现相同的异常吗?

我正在使用oracle jdk 1.8.0_40

编辑:

首先,感谢您的回答,现在对我来说很清楚了。我只想指出,我知道如何使用线程安全预防措施来避免程序中断,但我不知道为什么在给定的情况下会特别抛出此异常。托马斯在下面的评论中很好地解释了这一点。在接受的答案中也有很好的解释。再次感谢:)。


答案 1

我还在你的代码中发现了同样的异常。我在方法上添加了一个修饰符,错误似乎停止发生。问题是两个线程同时修改同一映射。必须转换一个对象才能将条目放入。但是,第二个线程正在处理一个突变的对象,该对象会引发一个 .因此,请确保没有两个线程同时访问同一映射。如果另一个线程执行相同的操作,则修饰符会阻止所有其他线程对类/实例执行任何操作。同步静态方法同步类本身,而同步的非静态方法仅同步类的实例。synchronizedputEntriesToMap()ClassCastExceptionsynchronized


答案 2

我对HashMap.computeIfAbsent的并发调用也是如此。我通过将实现更改为使用 ConcurrentHashMap 进行了修复。ClassCastException