并行流中的 Mockito when() 意外结果
Mockito when() in parallel stream unexpected result
我在我们的一项测试中调查 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(不是 nessaseraly 排序的):
我收到的输出:
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
请注意,这不一定按顺序发生,甚至不一定按这个顺序发生:
- 其中的某些部分可能实际上是同时发生在处理器的不同内核中。
- 虽然单个线程中的任何步骤总是 运行 有序,但它们可能 运行 在任何其他线程的任何步骤之前、之后或交错。
因为线程是 运行ning 并行的,而且您没有设置任何东西来管理它,所以无法保证这些调用发生的顺序:您的输出可能很容易已经 1 1 1
、1 2 3
、3 2 1
或 3 2 2
或 1 1 3
等
我在我们的一项测试中调查 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(不是 nessaseraly 排序的):
我收到的输出:
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
请注意,这不一定按顺序发生,甚至不一定按这个顺序发生:
- 其中的某些部分可能实际上是同时发生在处理器的不同内核中。
- 虽然单个线程中的任何步骤总是 运行 有序,但它们可能 运行 在任何其他线程的任何步骤之前、之后或交错。
因为线程是 运行ning 并行的,而且您没有设置任何东西来管理它,所以无法保证这些调用发生的顺序:您的输出可能很容易已经 1 1 1
、1 2 3
、3 2 1
或 3 2 2
或 1 1 3
等