2016-08-30 58 views
0

私は、この変数にvolatileキーワードを使用していないときに、たびに共通の変数 "n" 、私は理解できない何かが起こる、SB説明が助けてください、スニペットは次のようなものです:JAVAのスレッド間で変数を共有する

public class TwoThreads { 

private static int n = 0; 
private static int called = 0; 

public static void main(String[] args) { 

    for (int i = 0; i < 1000; i++) { 
     n = 0; 
     called = 0; 

     TwoThreads two = new TwoThreads(); 
     Inc inc = two.new Inc(); 
     Dec dec = two.new Dec(); 
     Thread t = new Thread(inc); 
     t.start(); 
     t = new Thread(dec); 
     t.start(); 
     while (called != 2) { 
      //System.out.println("----"); 
     } 
     System.out.println(n); 

    } 
} 

private synchronized void inc() { 
    n++; 
    called++; 
} 

private synchronized void dec() { 
    n--; 
    called++; 
} 

class Inc implements Runnable { 
    @Override 
    public void run() { 
     inc(); 
    } 
} 

class Dec implements Runnable { 
    @Override 
    public void run() { 
     dec(); 
    } 
} 

}

1)私は、実行後の "n = 0、= 2と呼ばれる" されて期待していますどのような、しかし、メインスレッドはwhileループでブロックされる可能性があります。

2)しかし、私はこの行、予想通りにプログラムコメントを解除する場合:

//System.out.println("----"); 

3)私は「と呼ばれる」オン「揮発性」を使用する必要があります知っているが、上記のはなぜ起こるか私が説明することはできませんが。

4)「呼び出された」とは、特定のスレッドの作業メモリで「読み込みと読み込み」ですが、「長い」ループの後にメインスレッドに「ストアアンドライト」しないのはなぜですか? print "行はそのような違いを生む可能性があります。

+1

print()の代わりにThread.sleep()メソッドを使用しても同じ効果が得られると思います。 –

+0

@victorあなたは正しいです、どんな行でも同じ効果があります、理由を説明できません –

+0

あなたはスレッドが実行されているときは制御しません。メインは実行を継続し、終了するまでにもう一方のスレッドも終了します。そのため、スレッドの残りの部分が完了するまで、Thread.join()を使用してメインを停止する必要があります。 (Thread.sleep()は時間がかかりますが、join()はfinnishまで待機します) –

答えて

2

データの書き込みはincとdecで行い、データの読み込みはメインでは行いません。 BOTHは予測可能な効果を得るために同期されるべきです。さもなければ、mainはincとdecによって行われた変更を決して見ることができない可能性があります。

+0

Vasanに感謝しますが、データの読み込みを同期させるには –

+0

これについては、コード全体を参照する必要があります。これにはさまざまなアプローチがあります。基本的には、読み取りと書き込みのコードを同期ブロックに入れ、共通のロックオブジェクトを使用します。 mainは静的メソッドであるため、そこで読みたい場合は、クラスオブジェクトまたは静的クラスレベル変数のみを使用できます。例: 'synchronized(TwoThreads。クラス){ '' //が読み取らnおよび/またはhere' 'と呼ばれる} ' '同期(TwoThreads.class){ '' //書き込みN及び/又はhere' 呼ば '}' – Vasan

0

++が実行される場所がわからない場合、メインスレッドは相互排除を行う新しいスレッドを生み出し続けます。メソッドが同期されているため、毎回++と呼ばれるスレッドは1つしか作成できませんそれぞれ正確にスレッドがそれを知っている。 n ++またはn--を2回実行することがあります。これはわからないので、メインスレッドがあなたの状態に達するまで、n ++を10回実行することがあります。

とあなたがここにcalledへのアクセスを同期するために必要なデータレース

while (called != 2) { 
      //System.out.println("----"); 
} 

//.. place for data race, n can be changed 

System.out.println(n); 
+0

おかげseregaそれ以外の場合はwhileループでブロックされる可能性があるため、メインスレッド以外では最大2つのスレッド(incとdec)が実行されることが予想されます。 –

+0

ループがブロックされている間は、その目的のためにcalled> 2を使用することはできません。なぜならメインスレッドがループ中に+++ 1000回呼び出すことができるからです。 incとdecスレッドが++と呼ばれる前にwhileループに達する可能性があり、条件がうまくいかず、新しいスレッドの別のペアが得られるでしょう。 –

0

について読みしてみてください:

while (called != 2) { 
    //System.out.println("----"); 
} 

私はgetCalled方法

private synchronized int getCalled() { 
    return called; 
} 

を追加し、called != 2を交換するsugest getCalled() != 2

なぜこの問題が発生するのか興味があれば、Javaメモリモデルのコンテキストで可視性について読むことができます。

+0

talexありがとう、私はグーグルだが、なぜ "System.out.println(" ---- ") ; "ジョブが完了し、データが同期バックされるとき.... –

+0

内部に同期があり、この同期によって一部のキャッシュがフラッシュされるためです。これが今後変更されないという保証はありません。 – talex

関連する問題