2016-10-16 4 views
1

3つの異なる方法で1つのインスタンスに対して3つのメソッドを実行しようとしています。具体的にはThreadsです。つまり、は、methodBmethodBの前に呼び出され、methodCの前に呼び出される必要があります。奇妙なマルチスレッドの振る舞い

Iは最初のコードで洗練されていないアプローチを試みた:

public void testFoo() 
{ 
    Foo foo = new Foo(); 
    ExecutorService service = Executors.newFixedThreadPool(3); 
    service.submit(() -> foo.callMethodB()); 
    service.submit(() -> foo.callMethodC()); 
    service.submit(() -> foo.callMethodA()); 
    service.shutdown(); 
} 

class Foo 
{ 

    boolean methodAcompleted; 
    boolean methodBcompleted; 

    void callMethodA() 
    { 
     this.methodA(); 
     methodAcompleted = true; 
    } 

    void callMethodB() 
    { 
     while (true) 
     { 
      if (methodAcompleted) 
      { 
       this.methodB(); 
       methodBcompleted = true; 
       break; 
      } 
     } 
    } 

    void callMethodC() 
    { 
     while (true) 
     { 
      if (methodBcompleted) 
      { 
       this.methodC(); 
       break; 
      } 
     } 
    } 

    void methodA() 
    { 
     try { 
      Thread.sleep(100); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("Method A completed!"); 
    } 

    void methodB() 
    { 
     try { 
      Thread.sleep(100); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("Method B completed!"); 
    } 

    void methodC() 
    { 
     try { 
      Thread.sleep(100); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("Method C completed!"); 
    } 
} 

私は次の奇数の観察に来た:のみ"Method A completed!"がプリントアウトされた上記のようなコードを実行

1)。ブール変数をstaticに変更しても同じ結果が得られます。 2)私はvolatileにブール変数を変更する場合は、すべてのステートメントが正しい順序で印刷されています?私の知る限りそれが保証(または不可能れていないので、これは、期待されている知っているように

"Method A completed!" 
"Method B completed!" 
"Method C completed!" 

Threadこと同じインスタンス上で別のThreadアクションによって変更が通知されます。変数がstaticの変数で実行されたとしても同じですか?

3)私は彼らがとして実行持つメソッドから遅延Thread.sleep()を削除するとき、それは奇妙次のようになります。この場合

void methodA() 
{ 
     System.out.println("Method A completed!"); 
} 

void methodB() 
{ 
     System.out.println("Method B completed!"); 
} 

void methodC() 
{ 
     System.out.println("Method C completed!"); 
} 

文がケース2のように正しい順序で常に印刷されています

"Method A completed!" 
"Method B completed!" 
"Method C completed!" 

Threadと同じではないThreadの変更を参照できますか?volatile変数? case 1case 3の異なる動作/結果についての説明は何ですか?

アップデート:私はatomic typesの目的があることが説得しています

class Foo { 
    AtomicBoolean methodAcompleted = new AtomicBoolean(false); 
    AtomicBoolean methodBcompleted = new AtomicBoolean(false); 

    ... 
} 

私は、上記のアプローチが不可欠であれば、最善の解決策としてAtomicBoolean代わりのプリミティブを使用することだと思いますそのような場合に一貫した行動を保証する。

答えて

2

booleanフィールドの値をJITでインライン化しないようにするには、メモリバリアが必要です。

JITは、現在のスレッドでフラグを変更せず、インライン化することができます。これにより、変更が見られなくなります。

volatileまたは​​メソッド(空の同期ブロックでも)を追加すると、この最適化が妨げられます。注:System.out.printlnは同期されています。

同じスレッド変数で動作する別のスレッドからの変更をスレッドが参照することはできますか?これは、と、JITがコードを最適化しました前にフラグを変更した場合、この最適化などの他のタイプの変更はまた、(あなたのJVMの実装に応じて)他のタイプに

を適用されない場合がありますが表示されますすることができます

変更が表示されます。

私は数年前にこの記事を書いていますが、詳細はこちらhttp://vanillajava.blogspot.co.uk/2012/01/demonstrating-when-volatile-is-required.html