java中如何证明HashMap不是线程安全的 [英] How to prove that HashMap in java is not thread-safe

查看:26
本文介绍了java中如何证明HashMap不是线程安全的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm working on an application, that has uses a HashMap to share state. I need to prove via unit tests that it will have problems in a multi-threaded environment.

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

I tried to check the state of the application in a single thread environment and in a multi-threaded environment via checking the size and elements of the HashMap in both of them. But seems this doesn't help, the state is always the same.

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

Are there any other ways to prove it or prove that an application that performs operations on the map works well with concurrent requests?

推荐答案

这很容易证明.

哈希映射基于一个数组,其中每个项目代表一个桶.随着更多键的添加,存储桶会增长,并且在某个阈值下,数组以更大的大小重新创建,以便其存储桶分布更均匀(性能考虑).在数组重新创建期间,数组变为空,这导致调用者的结果为空,直到重新创建完成.

A hash map is based on an array, where each item represents a bucket. As more keys are added, the buckets grow and at a certain threshold the array is recreated with a bigger size so that its buckets are spread more evenly (performance considerations). During the array recreation, the array becomes empty, which results in empty result for the caller, until the recreation completes.

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

It means that sometimes HashMap#put() will internally call HashMap#resize() to make the underlying array bigger.

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

HashMap#resize() assigns the table field a new empty array with a bigger capacity and populates it with the old items. While this population happens, the underlying array doesn't contain all of the old items and calling HashMap#get() with an existing key may return null.

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

The following code demonstrates that. You are very likely to get the exception that will mean the HashMap is not thread safe. I chose the target key as 65 535 - this way it will be the last element in the array, thus being the last element during re-population which increases the possibility of getting null on HashMap#get() (to see why, see HashMap#put() implementation).

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.");
    }
}

一个线程向地图添加新键.另一个线程不断检查 targetKey 是否存在.

One thread adds new keys to the map. The other thread constantly checks the targetKey is present.

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

If count those exceptions, I get around 200 000.

这篇关于java中如何证明HashMap不是线程安全的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆