我有一个多线程 Java 程序,其中每个线程都将唯一的键插入共享的
HashMap
中。但是,当我在所有线程执行完毕后检查HashMap
的大小时,大小与预期的插入数量不一致。
这是代码:
public class CurrentHashMapDemo {
private static final int NUM_THREADS = 5;
private static final int NUM_INSERTIONS = 100;
private static HashMap<String, Integer> hashMap = new HashMap<>();
public static void main(String[] args) throws InterruptedException{
ExecutorService executorService = Executors.newFixedThreadPool(NUM_THREADS);
for(int i=0; i< NUM_THREADS; i++){
executorService.execute(insertRecord());
}
executorService.shutdown();
if(!executorService.isTerminated()){
Thread.sleep(1000);
}
System.out.println("Size of the hashmap="+ hashMap.size());
}
private static Runnable insertRecord(){
return () -> {
for(int i=0; i<NUM_INSERTIONS; i++){
System.out.println("Key:"+ i+Thread.currentThread().getName());
hashMap.put(i+Thread.currentThread().getName(), i);
}
};
}
}
在此代码中,我创建了
NUM_THREADS
数量的线程,每个线程都将 NUM_INSERTIONS
数量的记录插入到 HashMap
中。每条记录的键是循环索引i
和当前线程名称的组合,对于每个线程来说应该是唯一的。
但是,当我在所有线程执行完毕后打印
HashMap
的大小时,大小并不一致NUM_THREADS * NUM_INSERTIONS
。我知道 HashMap
不是线程安全的,并且我知道我可以使用 ConcurrentHashMap
来解决这个问题。但我有兴趣了解导致这种不一致行为的 HashMap
实现中到底发生了哪些竞争条件。
我正在使用 OpenJDK 17。谁能解释一下在这个特定 JDK 的 HashMap 实现中可能发生这种竞争条件的位置以及它如何影响 HashMap 的大小?
任何人都可以解释这种竞争条件可能发生在哪里以及它如何影响
HashMap
的大小?
感谢@user85421您的评论,我正在寻找相同的,
一种可能性:哈希映射的创建容量为 16,当容量耗尽时,必须重新组织(调整大小) - 如果两个线程同时执行此操作,则可能会出现混乱
这只是为了将评论中每个人所说的(正确的!)变成答案。 (因为你似乎想要继续。)
您的演示代码的问题是它不是线程安全的。您有多个线程正在更新指定为非线程安全的共享对象(
HashMap
)实例。这将导致未指定的行为。
您所看到的不一致行为的确切(详细)原因是“无关紧要的”。你认为演示“应该有效,因为”的论点与它不起作用的证据相悖! 修复(在实际应用程序中)是在外部同步
HashMap
,或使用
ConcurrentHashMap
。