2011-09-16 21 views
6

競合状態を引き起こす単体テストを書かなければならないので、後で問題を修正したかどうかをテストすることができます。 問題は、競合状態が非常にまれにしか発生しないことです。おそらく、コンピュータに2つのコアしかないためです。Javaで競合状態を呼び起こす

class MyDateTime { 
    String getColonTime() { 
    // datetime is some kind of lazy caching variable declared somewhere(does not matter) 
    if (datetime == null) { 
     initDateTime(); //Uses lazy to initlialize variable, takes some time 
    } 
    // Colon time stores hh:mm as string 
    if (datetime.colonTime == null) { 
     StringBuilder sb = new StringBuilder(); 
     //Now do some steps to build the hh:mm string 
     //... 
     //set colon time 
     datetime.colonTime = sb.toString(); 
    } 
    return datetime.colonTime; 
    } 
} 

説明:: initDateTimeはdateTimeの新しいインスタンスを割り当て、そのため、我々はそれが怠惰な初期化したいと、私が述べたようにdatetime.colonTimeが、(その後nullである

コードは、次のようなものです前)。 スレッドAがメソッドに入り、スケジューラがinitDateTime()を実行する直前にスレッドAを停止すると、 Thread Bがrunst getColonTime()を呼び出すと、datetimeがまだnullであり、それが初期化されていることがわかります。 datetime.colonTimeはnullであるため、2番目のブロックは実行され、datetime.colonTimeはStringBuilderの値を取得します。 スケジューラがこの行とreturn文の間でスレッドを停止し、スレッドAを再開すると、次のようになります。 initDateTimeが呼び出される直前にAが停止したので、AはのinitDateTime datetime.colonTimeを再びnullに設定します。スレッドAは2番目のifブロックを入力しますが、スケジューラはdatetime.colonTime = sb.toString()の前にAを中断します。が呼び出されます。結論として、dateTime.colonTimeはまだnullです。 スケジューラはBを再開し、メソッドはnullを返します。

getColonTime()をMyDateTimeの単一(最終)インスタンスに呼び出す多数のスレッドを持つことによって競合状態を引き起こそうとしましたが、まれにしかまれにしか失敗しませんでした。( JUnit "test"?

+3

まず競合状態を引き起こすためにデバッガを使用することができます。私。スレッドを開始し、いくつかのブレークポイント(ifの間のようなもの)にキャッチして、別のブレークポイントを開始します。 RCがどのように起こったのかを知った後(あなたは今のところそれがないようです)、成功した単体テストを書くことができます – pupssman

+0

私はあなたが 'return datetimeに到達する方法を見ていません。colonTime; 'を返し、nullを返します。あなたはhh:mm文字列をどのように構築しているかに問題はないと確信していますか?たぶんあなたの質問にそのコードを追加して、それを見ることができます。 – Windle

+0

なぜそれが起こることができるか説明しました。それほど明白ではないと認めなければなりません。 – user3001

答えて

4

Thread Weaverを参照するか、マルチスレッドコードをテストするための他のフレームワークがあるかもしれません。私はそれを使用していませんが、Users' Guideはまさにこの種のテスト用に設計されているかのように見えます。

+0

私はすでにそれを見ましたが、私はどれくらい適しているのか分かりません。その経験は誰ですか? – user3001

+0

私はスレッドウィーバーを試みました。問題がどこにあるか知っていれば、テストに役立ちます。しかし、メソッドの行に対してN x Nのテストを行うことはできません(私はディスカッショングループで質問しました)。 – user3001

7

あなたが言及しているように、競合状態は一貫して再現するのが非常に難しいですが、平均の法則があなたの側にあります。 TDDの原則に従えば、以前のようにコードを開始し、十分な時間テストを試してみる必要があります古いコードと一貫して失敗する新しいコードに変更して、それが失敗しないことを確認してください。

+0

テストが失敗する可能性は高くなりますが、テストを実行するのにかかる時間が長くなるほど高い可能性があります。ユニットテストは速くなるはずです。 (しかし私は良いアイデアはありません) –

0

私はこの投稿がかなり古いと知っていますが、私は同様の状況に直面しています。私がする傾向があるのは、睡眠を伴う競争状態を好むことです。あなたのケースでは

、私は

class MyDateTime { 
     String getColonTime() throws InterruptedException{ 
      if (datetime == null) { 
       Thread.sleep(new Random().nextInt(100); //Wait to enhance the chances that multiple threads enter here and reset colonTime. 
       initDateTime(); 
      } 
      Thread.sleep(new Random().nextInt(100); //Wait to enhance the chances that colonTime stays null for a while. 
      if (datetime.colonTime == null) { 
       StringBuilder sb = new StringBuilder(); 
       datetime.colonTime = sb.toString(); 
      } 
      Thread.sleep(new Random().nextInt(100); //Wait to favour reset of colonTime by another thread in the meantime. 
      return datetime.colonTime; 
     } 
    } 

ような何かをするだろう。しかし、明らかに、これは非常に急速に厄介になります。私はいくつかの "ブレークポイント"が与えられたすべてのパスをスケジューラが探索するように強制するいくつかの方法があることを望みます。

投稿が少し古いので、Javaで競合状態をテストする良い方法が見つかったのかどうか疑問に思っていました。共有するアドバイスはありますか?

ありがとう

+1

私が受け入れられた答えで言ったように、スレッドウィーバーは素晴らしいツールであり、今は他のものがあると思います。たとえば、IntelliJは正しくリコールすると、マルチスレッドデバッグのサポートを受けました。また、統計はあなたの側にあります:十分なテストを頻繁に繰り返す場合は、しばらくして競合状態になるでしょう:) https://github.com/junit-team/junit4/wiki/Multithreaded-codeも参照してください。並行性 – user3001

関連する問題