1

我有一个类(我们称之为 XClass),它有一个方法(我们称之为 xMethod),我正在测试它。它还包含一个:

private static Map<String, String> map = new HashMap<String, String>();

在我的单元测试设置方法中,我有:

ReflectionTestUtils.setField(xClass, "map", map, null);

在测试方法中,我创建了几个(在我的情况下为 8 个)线程。他们的 run 方法调用 xClass.xMethod。此方法更改静态地图变量。方法 xMethod 应该调用 map.containsKey() map.get() 和 map.put() 8 次。它不会做任何删除。此方法也不会创建任何新线程,因此一旦线程使用 xMethod 完成,就不应更改映射。我等待所有线程完成(通常或异常)。比我查地图

int mapSize = map.size();
assertEquals("map:" + map, 8, mapSize);

它在这里失败并显示以下消息:

java.lang.AssertionError: map:{3=x1, 2=x2, 1=x3, 7=x4, 6=x5, 5=x6, 4=x7, 8=x8} 预期:<8> 但是是:< 7>

我通过使用 ConcurrentHashMap 解决了这个问题,但我仍然对这个问题感到困惑。在所有 8 个线程完成后,地图怎么可能表现得很奇怪(size() 返回 7 但 toString() 打印 8 个实体)?如果有 7 个实体并且 size() 方法给出 7 个,我会理解的,但地图中有 8 个实体。这怎么可能?!

顺便说一句,我通过以下几种方式检查了线程的终止:
1. 检查 Thread.State.TERMINATED。
2.我在方法返回之前打印了一条简单消息,在线程完成后打印了一条简单消息(并检查了没有抛出异常。8条消息总是在第9条之前写入(在线程“完成”后的测试中)。
3.甚至做了一个简单的线程,在run方法中包含以下逻辑:

        public void run() {
                try {
                        obj = xClass.xMethod();
                } catch (Exception e) {
                        exc = e;
                }
                finished = true;
        }

比我只会无限循环,直到所有线程都完成== true。

所有这些都意味着在我继续断言和检查映射之前线程已经完成。那么,map.size() 是否有可能返回 7 和 map.toString() 返回 8 个实体?

亲切的问候,
暴君

4

1 回答 1

8

通过同时改变地图,您破坏了其内部不变量,并且您正在观察未指定和不可预测的行为,这是预期的结果。

于 2013-01-16T14:58:23.347 回答