2012-02-08 14 views
26

の使用ので、私は​​キーワードでテストしていました。ここに私が試した例です。学習Java、同期キーワード

public class MyTest { 
    static int i = 0; 
    public static void main(String[] args) { 
     new Thread(t1).start(); 
     new Thread(t2).start(); 
    } 

    private static void countMe(String name){ 
     i++; 
     System.out.println("Current Counter is: " + i + ", updated by: " + name); 
    } 

    private static Runnable t1 = new Runnable() { 
     public void run() { 
      try{ 
       for(int i=0; i<5; i++){ 
        countMe("t1"); 
       } 
      } catch (Exception e){} 

     } 
    }; 

    private static Runnable t2 = new Runnable() { 
     public void run() { 
      try{ 
       for(int i=0; i<5; i++){ 
        countMe("t2"); 
       } 
      } catch (Exception e){} 
     } 
    }; 
} 

私はそれを実行すると、二つのスレッドからcountMe()メソッドを呼び出すの出力は、この出力を生成します。

Current Counter is: 1 
Current Counter is: 2 
Current Counter is: 4 
Current Counter is: 5 
Current Counter is: 6 
Current Counter is: 7 
Current Counter is: 3 
Current Counter is: 8 
Current Counter is: 9 
Current Counter is: 10 

そして私は方法countMe()を変更します。

private synchronized static void countMe(){ 
     i++; 
     System.out.println("Current Counter is: " + i); 
} 

私はこの出力を得る:

Current Counter is: 1 
Current Counter is: 2 
Current Counter is: 3 
Current Counter is: 4 
Current Counter is: 5 
Current Counter is: 6 
Current Counter is: 7 
Current Counter is: 8 
Current Counter is: 9 
Current Counter is: 10 

これは私に​​の目的を明確に理解してくれますが、他にも理由があることを知りたいので、​​を使用することができます。または私がここでやったことは、なぜこの​​キーワードを使う必要があるのか​​?

ありがとうございました。

EDIT:私が混乱しているもう一つの点は、カウンタが7の後に3になった理由です。私には不可能なようですが、試してみると同じような結果が起こります。 ?

+0

スレッドが交代する保証はありません。他のスレッドが7にカウントされるまで、3を数えたスレッドが印刷されないことがあります。 –

+0

ありがとうございます。しかし、7より後で次の呼び出しを印刷することはできますか?3、6または5を印刷できます。それが3だった後に私は読んでいない、これは非常に奇妙に聞こえる。だから、私は2つ以上のスレッドから同じコードブロックを使用する必要があるたびに常に同期を使用することが賢明でなければならないと思いますか? – 911TurboS

+5

スレッドの名前を印刷することをお勧めします。混乱がどこにあるのかがより明らかになります。並行処理は複雑なトピックであり、単純な答えはありません。私の好みは、アクターのパターンを使用し、可能な限りスレッド間でデータをほとんど共有しないことです。理想的には、なし。非常に効率的にコードを実行する1つのスレッドは、通常は十分です。 –

答えて

24

2つのこと:

まず、それがインターリーブする同じオブジェクトの同期メソッドの2つの呼び出しは可能ではありません。あるスレッドがオブジェクトの同期メソッドを実行しているとき、そのオブジェクトで最初のスレッドが完了するまで、同じオブジェクトブロックの同期メソッドを呼び出す他のすべてのスレッド(実行を中断)。

第2に、同期化されたメソッドが終了すると、同期化されたメソッドの後続の呼び出しと自動的に同じオブジェクトの同期化された関係が確立されます。これにより、オブジェクトの状態の変更がすべてのスレッドで認識できることが保証されます。

同期メソッドを使用すると、スレッドの干渉やメモリーの一貫性のエラーを防止する簡単な方法が使用できます。複数のスレッドにオブジェクトが表示されている場合、そのオブジェクトの変数に対する読み取りまたは書き込みはすべて同期メソッドによって行われます。 (重要な例外:オブジェクトの構築後に変更できない最終フィールドは、オブジェクトが構築されると非同期メソッドで安全に読み取ることができます)。

ソース:http://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html

+0

vulkaninoの場合、同じメソッドにアクセスするスレッドを使用する場合は、毎回synchronizedを使用することをお勧めします。 – 911TurboS

+0

+1、私よりも良い答え... – Nim

+0

@ 911TurboSいくつかのメソッドで 'synchronized'を使うだけで、マルチスレッドコードを正しく同期させるには非常に不十分です。あなたはデータの一貫性(およびおそらく他の多くのもの)に気づく必要があります。 – toto2

11

Vulkaninoはあなたの主な質問に良い答えを与えたので、実際にそこにあるので、私は唯一の3が7後に印刷することができ、約3印刷7.

後、あなたの質問に対処しますあなたのステートメントの中にJavaコードよりも多くのバイトコードがあります。

私はそれを拡張します。

あなたが

System.out.println("Current Counter is: " + i); 

を呼び出し、それがJavaコードの1行で発生しますが、実際に何が起こるかは、作成された文字列と、その文字列はprintlnをすることが渡されています。 printlnメソッド自体は、実際に行をコンソールに書き込む前に少しの処理を行う必要があります。

概念的には、次のようなことが起こっています。

String printlnString = "Current Counter is: 3" 
--> maybe the other thread executes here 
System.out.println(printlnString); 
--> or maybe the other thread executes here 
i is now equal to 7 and the console has "Current Counter is: 7" 
println writes "Current Counter is: 3" to console 
+0

カウンタをoutに書き込むのではなく、単純な配列に書き込んでから、スレッドが終了したら配列を出力してください。 – cognacc