Mockito when() 在并行流中出现意外结果



我正在调查Mockito在我们的一个测试中的行为,我不了解它的行为。以下代码片段显示了相同的行为:

@Test
public void test(){
var mymock = Mockito.mock(Object.class);
var l = List.of(1,2,3);
l.parallelStream().forEach(val -> {
int finalI = val;
doAnswer(invocationOnMock -> {
log.info("{}",finalI);
return null;
}).when(mymock).toString();
mymock.toString();
});
}

我期望的输出是按某种顺序打印 1,2,3 的打印件(未按顺序排序):

我收到的输出:

2
2
2

为什么我得到这个输出?

这是一个经典的争用条件:您的并行流并行执行forEachlambda 三次。在这种情况下,所有三个线程都设法执行了doAnswer()调用,然后它们中的任何一个都进入了mymock.toString()行。碰巧val=2的执行最后运行。令人高兴的是,Mockito是相当线程安全的,所以你不会只得到抛出的异常。

可能发生这种情况的一种方式是线程碰巧像这样运行:

  1. 主线程:调用l.parallelStream().forEach(),生成 3 个线程,val等于 1、2 和 3
  2. val=1 线程:执行finalI=val,在此线程中给出值 1
  3. val=2 线程:执行finalI=val,在此线程中给出值 2
  4. val=3 线程:执行finalI=val,在此线程中给出值 3
  5. val=3 线程:调用doAnswer(...),将来调用mymock.toString()打印出3
  6. val=1 线程:调用doAnswer(...),将来调用mymock.toString()打印出1
  7. val=2 线程:调用doAnswer(...),将来调用mymock.toString()打印出2
  8. val=1 线程:调用mymock.toString(),打印出2
  9. val=2 线程:调用mymock.toString(),打印出2
  10. val=3 线程:调用mymock.toString(),打印出2

请注意,这不一定按顺序发生,甚至不一定按顺序发生:

  • 其中的某些部分可能同时发生在处理器的不同内核中。
  • 虽然单个线程中的任何步骤始终按顺序运行,但它们可能在任何其他线程的任何步骤之前、之后或交错运行。

因为线程是并行运行的,并且你没有采取任何措施来管理它,所以不能保证这些调用发生的顺序:你的输出可以很容易地被1 1 11 2 33 2 13 2 21 1 3等。

最新更新