私は単純なJavaマルチスレッドテストをやろうとしています。そして、私がSpringBootTestを使用するかどうかによって、同じ関数が異なる動作をします。コードは以下の通りである:SpringBootTestで匿名スレッドが通常のユニットテストより速く完了したのはなぜですか?
ターゲットクラス:フーの
public class Foo {
private int i = 0;
public void setI() {
new Thread(() -> i = 1).start();
}
public int getI() {
return i;
}
}
RESTインターフェース:
@RestController
public class FooController {
private static Foo foo = new Foo();
@RequestMapping(path = "getFoo")
public int getFoo() {
return foo.getI();
}
@RequestMapping(path = "setFoo")
public void setFoo() {
foo.setI();
}
}
とテストクラス:
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FooControllerTest {
@LocalServerPort
private int port;
@Autowired
private TestRestTemplate template;
@Test
public void noRest() {
Foo foo = new Foo();
foo.setI();
assertThat(foo.getI(), is(0));
}
@Test
public void rest() {
template.postForEntity("http://localhost:" + port + "/setFoo", null, Integer.class);
ResponseEntity<Integer> response = template.postForEntity("http://localhost:" + port + "/getFoo", null, Integer.class);
assertThat(response.getBody(), is(1));
}
}
クラスはFooで目的新しいスレッドを開始してiの値を設定することです。
スレッドの作成に時間がかかるため、テストケースnoTest()のメインスレッドは常にsetI()で作成された匿名スレッドの前に終了します。したがって、iの値は変更されません。
奇妙なことは、同じ関数をテストするためにRESTインターフェイスをトリガすると、iの値は常に新しく作成された匿名スレッドによって変更されるということです。これは本当に私を混乱させています。私は匿名スレッドがメインスレッドの前または後に終了することになる受給者はいないことを理解しています。しかし、ここでは、Springテストフレームワークがバックグラウンドで何かをしたので、匿名スレッドは常にメインスレッドよりも速く終了するようです。
誰かがこれがどうして起こったのか、私が何か誤解したのか、誰かが説明できることを願っています。ありがとうございました。
'private int i = 0;'をvolatileにする必要があります。または 'getI()'を同期させます。 – JimmyB
コードに潜在的な同期の問題があると私は理解していますが、なぜテスト結果が異なるのか、より興味があります。 BTW私は揮発性と同期を追加しようとし、結果は同じstilです。 – Will
volatile/synchronizedテクニックは、 'getI()'の結果が最初にスレッドの実行進捗状況を反映しているという前提の前提条件です。 – JimmyB