【问题标题】:Strange HashMap exception (HashMap$Node cannot be cast to HashMap$TreeNode)奇怪的 HashMap 异常(HashMap$Node 无法转换为 HashMap$TreeNode)
【发布时间】:2015-07-10 03:15:53
【问题描述】:

在 stackoverflow 上提出另一个问题后, (Java- Why this program not throwing concurrent Modification exception) 我开始尝试使用 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

编辑:

首先,感谢您的回答,我现在很清楚了。 我只想指出,我知道如何通过使用线程安全预防措施来避免程序中断,但我不知道为什么在特定情况下会引发此异常。 Thomas 在下面的 cmets 中已经很好地解释了这一点。在接受的答案中也很好地解释了这一点。再次感谢:)。

【问题讨论】:

  • 这只是“没有正确使用并发”的结果——HashMap 需要外部同步才能保证线程安全。
  • 有可能,当你不在不同线程之间同步时,这里的问题是什么?
  • 问题是,为什么异常如此奇怪。在查看 HashMap 代码后,似乎 Node 对象是在 TreeNode 上强制转换的,它不是 TreeNode 实例。这就是抛出异常的原因。但是为什么只有在错误使用并发的情况下才会发生呢?
  • 如您所见,Java 8 的 HashMap 有一个名为 treeify 的方法来改进内部存储。由于您没有以线程安全的方式使用地图(正如其他人已经评论过的那样),一个线程依赖于 TreeNode 类的条目,而另一个很可能将相同的引用更改为 Node 类的条目(都扩展Map.Entry)。

标签: java hashmap


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    我在同时调用HashMap.computeIfAbsent 时得到了相同的ClassCastException。我通过将实现更改为使用 ConcurrentHashMap 来修复。

    【讨论】:

      【解决方案3】:

      我解决了这个问题,只是清理并构建到项目中,它可以工作

      【讨论】:

      • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
      猜你喜欢
      • 2019-02-17
      • 2017-08-14
      • 1970-01-01
      • 2011-07-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多