我正在调查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
为什么我得到这个输出?
这是一个经典的争用条件:您的并行流并行执行forEach
lambda 三次。在这种情况下,所有三个线程都设法执行了doAnswer()
调用,然后它们中的任何一个都进入了mymock.toString()
行。碰巧val=2
的执行最后运行。令人高兴的是,Mockito是相当线程安全的,所以你不会只得到抛出的异常。
可能发生这种情况的一种方式是线程碰巧像这样运行:
- 主线程:调用
l.parallelStream().forEach()
,生成 3 个线程,val
等于 1、2 和 3 - val=1 线程:执行
finalI=val
,在此线程中给出值 1 - val=2 线程:执行
finalI=val
,在此线程中给出值 2 - val=3 线程:执行
finalI=val
,在此线程中给出值 3 - val=3 线程:调用
doAnswer(...)
,将来调用mymock.toString()
打印出3
- val=1 线程:调用
doAnswer(...)
,将来调用mymock.toString()
打印出1
- val=2 线程:调用
doAnswer(...)
,将来调用mymock.toString()
打印出2
- val=1 线程:调用
mymock.toString()
,打印出2
- val=2 线程:调用
mymock.toString()
,打印出2
- val=3 线程:调用
mymock.toString()
,打印出2
请注意,这不一定按顺序发生,甚至不一定按此顺序发生:
- 其中的某些部分可能同时发生在处理器的不同内核中。
- 虽然单个线程中的任何步骤始终按顺序运行,但它们可能在任何其他线程的任何步骤之前、之后或交错运行。
因为线程是并行运行的,并且你没有采取任何措施来管理它,所以不能保证这些调用发生的顺序:你的输出可以很容易地被1 1 1
、1 2 3
、3 2 1
或3 2 2
或1 1 3
等。