java - 如何证明java中的HashMap不是线程安全的

标签 java multithreading unit-testing hashmap

我正在开发一个应用程序,它使用 HashMap 来共享状态。我需要通过单元测试来证明它在多线程环境下会有问题。

我尝试通过检查两者中 HashMap 的大小和元素来检查单线程环境和多线程环境中应用程序的状态。但这似乎无济于事,状态始终相同。

是否有任何其他方法可以证明或证明对 map 执行操作的应用程序可以很好地处理并发请求?

最佳答案

这很容易证明。

不久

HashMap 基于数组,其中每个项目代表一个桶。随着更多键的添加,桶会增长,并且在某个阈值时,数组会被重新创建,并具有更大的大小,以便其桶分布得更均匀(性能考虑)。在数组重新创建期间,数组变为空,这会导致调用方的结果为空,直到重新创建完成。

细节与证明

这意味着有时 HashMap#put() 会在内部调用 HashMap#resize() 以使底层数组变大。

HashMap#resize()table 字段分配一个容量更大的新空数组,并用旧项目填充它。虽然发生了这种填充,但底层数组不包含所有旧项目并且使用现有键调用 HashMap#get() 可能会返回 null.

下面的代码演示了这一点。您很可能会遇到异常,这意味着 HashMap 不是线程安全的。我将目标键选择为 65 535 - 这样它将成为数组中的最后一个元素,因此成为重新填充期间的最后一个元素,这增加了获得 null 的可能性> 关于 HashMap#get()(要了解原因,请参阅 HashMap#put() 实现)。

final Map<Integer, String> map = new HashMap<>();

final Integer targetKey = 0b1111_1111_1111_1111; // 65 535
final String targetValue = "v";
map.put(targetKey, targetValue);

new Thread(() -> {
    IntStream.range(0, targetKey).forEach(key -> map.put(key, "someValue"));
}).start();


while (true) {
    if (!targetValue.equals(map.get(targetKey))) {
        throw new RuntimeException("HashMap is not thread safe.");
    }
}

一个线程向 map 添加新键。另一个线程不断检查 targetKey 是否存在。

如果算上这些异常,我得到大约 200 000

关于java - 如何证明java中的HashMap不是线程安全的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18542037/

相关文章:

unit-testing - 在 Visual Studio Code 中调试 Go 测试

c++ - C++ 中的单元测试运算符<<

java - Apache 公共(public) CLI : how to prevent using short-name for options?

java - 未找到 fragment 类

在 C 中从 main 调用 void* 函数

.net - MSTest:断言线程执行

java - 多线程更新表面 View Canvas

angular - 使用 flush() 方法时 : "Automatic conversion to JSON is not supported for response type."

java - 在 Hibernate 中获取 JDBC 结果集

java - RecyclerView,继承自自定义的 RecyclerView adapter