web-dev-qa-db-ja.com

Javaの学習、同期キーワードの使用

だから私はsynchronizedキーワードでテストしていた。これが私が試した例です:

_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){}
       }
    };
}  
_

実行すると、2つのスレッドから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  
_

これにより、synchronizedの目的を明確に理解できますが、synchronizedを使用できる他の理由もあることを知りたいと思います。または、ここで行ったことは、このsynchronizedキーワードを使用する必要がある唯一の理由ですか?

ありがとう。

編集:私が混乱しているもう1つのことは、最初の出力でカウンターが7の後に3になった理由です。正常?

28
911TurboS

2つのこと:

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

第二に、同期メソッドが終了すると、同じオブジェクトに対する同期メソッドの後続の呼び出しと発生前の関係が自動的に確立されます。これにより、オブジェクトの状態の変更がすべてのスレッドから見えるようになります。

同期メソッドは、スレッドの干渉とメモリの一貫性エラーを防ぐための簡単な戦略を可能にします。オブジェクトが複数のスレッドから見える場合、そのオブジェクトの変数へのすべての読み取りまたは書き込みは同期メソッドを通じて行われます。 (重要な例外:オブジェクトの構築後に変更できない最終フィールドは、オブジェクトが構築されると、非同期メソッドを介して安全に読み取ることができます)。

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

26
vulkanino

Vulkaninoはあなたの主な質問に良い答えを与えたので、7の後の3印刷についてのあなたの質問にのみ対処します。

実際には、ステートメントにJavaコードよりも多くのバイトコードがあるため、3は7の後に印刷できます。

これについて詳しく説明します。

あなたが呼ぶ

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
11
David V