gpt4 book ai didi

java - HashMap 中唯一键的线程安全性

转载 作者:行者123 更新时间:2023-12-02 02:03:30 25 4
gpt4 key购买 nike

关于这个主题有很多讨论,例如这里:

What's the difference between ConcurrentHashMap and Collections.synchronizedMap(Map)?

但我还没有找到我的具体用例的答案。

通常,您不能假设 HashMap 是线程安全的。如果同时从不同的线程写入同一个键,一切都会崩溃。但是如果我知道我的所有线程都有唯一的键怎么办?

这段代码是线程安全的还是需要添加阻塞机制(或使用并发映射)?

Map<int, String> myMap = new HashMap<>();
for (int i = 1 ; i > 6 ; i++) {
new Thread(() -> {
myMap.put(i, Integer.toString(i));
}).start();
}

最佳答案

答案很简单:HashMap 根本不保证线程安全。

事实上it's explicitly documented它不是线程安全的:

If multiple threads access a hash map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally.

因此,在没有任何类型同步的情况下从多个线程访问一个线程将导致灾难。

见过每个线程使用不同关键原因问题的情况(例如迭代同时发生导致无限循环)。

想想重新散列:当达到阈值时,需要调整内部存储桶数组的大小。这是一个有点冗长的操作(与单个 put 相比)。在此期间,如果另一个线程也尝试put(甚至可能触发第二次重新哈希!),则可能会发生各种奇怪的事情。

此外,您没有可靠的方法来证明您的特定用例是安全的,因为您可以运行的所有测试都可能“意外”起作用。换句话说:你永远不能依赖这个工作,即使你认为你已经用单元测试覆盖了它。

由于并非所有人都相信,您可以使用以下代码轻松地自行测试:

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

class HashMapDemonstration {

public static void main(String[] args) throws InterruptedException {
int threadCount = 10;
int valuesPerThread = 1000;
Map<Integer, Integer> map = new HashMap<>();
List<Thread> threads = new ArrayList<>(threadCount);
for (int i = 0; i < threadCount; i++) {
Thread thread = new Thread(new MyUpdater(map, i*valuesPerThread, (i+1)*valuesPerThread - 1));
thread.start();
threads.add(thread);
}
for (Thread thread : threads) {
thread.join();
}
System.out.printf("%d threads with %d values per thread with a %s produced %d entries, should be %d%n",
threadCount, valuesPerThread, map.getClass().getName(), map.size(), threadCount * valuesPerThread);
}
}

class MyUpdater implements Runnable {
private final Map<Integer, Integer> map;
private final int startValue;
private final int endValue;

MyUpdater(Map<Integer, Integer> map, int startValue, int endValue) {
this.map = map;
this.startValue = startValue;
this.endValue = endValue;
System.out.printf("Creating updater for values %d to %d%n", startValue, endValue);
}

@Override
public void run() {
for (int i = startValue; i<= endValue; i++) {
map.put(i, i);
}
}
}

这正是OP提到的程序类型:每个线程只会写入其他线程从未接触过的键。尽管如此,生成的 Map 将不会包含所有条目:

Creating updater for values 0 to 999
Creating updater for values 1000 to 1999
Creating updater for values 2000 to 2999
Creating updater for values 3000 to 3999
Creating updater for values 4000 to 4999
Creating updater for values 5000 to 5999
Creating updater for values 6000 to 6999
Creating updater for values 7000 to 7999
Creating updater for values 8000 to 8999
Creating updater for values 9000 to 9999
10 threads with 1000 values per thread with a java.util.HashMap produced 9968 entries, should be 10000

请注意,最终Map中的实际条目数每次运行都会有所不同。它有时甚至会打印 10000 (因为它不是线程安全的!)。

请注意,这种故障模式(丢失条目)绝对不是唯一可能的模式:基本上任何事情都可能发生。

关于java - HashMap 中唯一键的线程安全性,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68710078/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com