2017-08-04 13 views
0

私は単純な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テストフレームワークがバックグラウンドで何かをしたので、匿名スレッドは常にメインスレッドよりも速く終了するようです。

誰かがこれがどうして起こったのか、私が何か誤解したのか、誰かが説明できることを願っています。ありがとうございました。

+1

'private int i = 0;'をvolatileにする必要があります。または 'getI()'を同期させます。 – JimmyB

+0

コードに潜在的な同期の問題があると私は理解していますが、なぜテスト結果が異なるのか、より興味があります。 BTW私は揮発性と同期を追加しようとし、結果は同じstilです。 – Will

+1

volatile/synchronizedテクニックは、 'getI()'の結果が最初にスレッドの実行進捗状況を反映しているという前提の前提条件です。 – JimmyB

答えて

0

私はこれを理解したと思うので、私自身の質問に答えさせてください。

最初の例(noRest())では、iの値を変更する新しいスレッド(スレッド2と呼ぶ)を作成しようとしています。しかし、私の環境の主なスレッドは、常にスレッド2よりも先にiを読むためのタイムスライスを取得します。

  1. メインスレッド:作成
  2. スレッド2:作成
  3. メインスレッド:私は読み出し
  4. スレッド2:セットnoRest Iにおいて

したがって結果(I CPUの順であると仮定します)。

そして、2番目の例のrest()では、メインスレッドはスプリングブートテストフレームワークの開始のようなやり方をする必要があり、CPUの順序が変更されます。

  1. メインスレッド::創造
  2. スレッド2:作成
  3. メインスレッド:INIT春ブーツテストフレームワーク
  4. スレッド2:私
  5. メインスレッドのセットを私の特定の環境では、常にこのように実行します:読み込みi

もちろん、実際のCPUスライスはもっと複雑です。しかし、私は、どちらのテストケースでも、注文がまったく問題ではないことを実証するだけで十分だと思います。他の環境で実行すると、結果が変わる可能性があります。両方のテストケースをより信頼できるものにする1つの解決策は、2つのスレッド間でThread.sleep()またはThread.yield()を使用して、一方のスレッドが常に他方のスレッドより前に終了することを確認することです。

そしてJimmyBさんのお役に立つコメントに感謝します。

関連する問題