1。同期の基本的な使用
Synchronizedは、Javaおよび最も簡単な方法で同時実行の問題を解決するために最も一般的に使用される方法です。 Synchronizedには、次の3つの主要な機能があります。(1)スレッド相互排他的なアクセス同期コード(2)共有変数の変更がタイムリーに見えることを確認します(3)並べ替えの問題を効果的に解決します。 Synchronizedには、同期の3つの用途があります。
(1)変更の通常の方法
(2)静的メソッドを変更します
(3)コードブロックを変更します
次に、これらの3つの使用方法を説明するためにいくつかの例プログラムを使用します(比較のために、同期の異なる使用方法を除き、他の3つのコードは基本的に一貫しています)。
1.同期なし:
コードスニペット1:
パッケージcom.paddx.test.concurrent; public class synchronizedtest {public void method1(){system.out.println( "method 1 start"); try {System.out.println( "Method 1 execute"); thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); } system.out.println( "Method 1 End"); } public void method2(){system.out.println( "方法2 start"); try {System.out.println( "Method 2 execute"); thread.sleep(1000); } catch(arturnedexception e){e.printstacktrace(); } system.out.println( "Method 2 End"); } public static void main(string [] args){final synchronizedtest test = new synchronizedTest();新しいスレッド(new runnable(){@override public void run(){test.method1();}})。start();新しいスレッド(new runnable(){@override public void run(){test.method2();}})。start(); }}実行結果は次のとおりです。スレッド1とスレッド2は、同時に実行状態を入力します。スレッド2はスレッド1よりも速く実行されるため、スレッド2は最初に実行されます。このプロセスでは、スレッド1とスレッド2が同時に実行されます。
方法1開始
方法1実行
方法2開始
方法2実行
方法2エンド
方法1エンド
2。一般的な方法の同期:
コードスニペット2:
パッケージcom.paddx.test.concurrent; public class synchronizedtest {public synchronized void method1(){system.out.println( "method 1 start"); try {System.out.println( "Method 1 execute"); thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); } system.out.println( "Method 1 End"); } public synchronized void method2(){system.out.println( "Method 2 start"); try {System.out.println( "Method 2 execute"); thread.sleep(1000); } catch(arturnedexception e){e.printstacktrace(); } system.out.println( "Method 2 End"); } public static void main(string [] args){final synchronizedtest test = new synchronizedTest();新しいスレッド(new runnable(){@override public void run(){test.method1();}})。start();新しいスレッド(new runnable(){@override public void run(){test.method2();}})。start(); }}実行結果は次のとおりです。コードセグメントと比較した後、Thread 2は、Method2メソッドの実行を開始する前に、スレッド1のMethod1の実行が完了するのを待つ必要があることが明確にわかります。
方法1開始
方法1実行
方法1エンド
方法2開始
方法2実行
方法2エンド
3。静的メソッド(クラス)同期
コードスニペット3:
パッケージcom.paddx.test.concurrent; public class synchronizedTest {public static同期Void method1(){system.out.println( "Method 1 start"); try {System.out.println( "Method 1 execute"); thread.sleep(3000); } catch(arturnedexception e){e.printstacktrace(); } system.out.println( "Method 1 End"); } public static同期void method2(){system.out.println( "Method 2 start"); try {System.out.println( "Method 2 execute"); thread.sleep(1000); } catch(arturnedexception e){e.printstacktrace(); } system.out.println( "Method 2 End"); } public static void main(string [] args){final synchronizedtest test = new synchronizedTest(); final synchronizedtest test2 = new synchronizedTest();新しいスレッド(new runnable(){@override public void run(){test.method1();}})。start();新しいスレッド(new runnable(){@override public void run(){test2.method2();}})。start(); }}実行結果は次のとおりです。静的方法の同期は、本質的にクラスの同期です(静的方法は、本質的にクラスの方法であり、オブジェクトのメソッドではありません)。したがって、Testとtest2が異なるオブジェクトに属している場合でも、どちらもSynchronizedTestクラスのインスタンスに属しているため、Method1とMethod2は連続的にのみ実行でき、同時に実行できません。
方法1開始
方法1実行
方法1エンド
方法2開始
方法2実行
方法2エンド
4.コードブロック同期
コードスニペット4:
パッケージcom.paddx.test.concurrent; public class synchronizedtest {public void method1(){system.out.println( "method 1 start"); try {synchronized(this){system.out.println( "Method 1 execute"); thread.sleep(3000); }} catch(arturnedexception e){e.printstacktrace(); } system.out.println( "Method 1 End"); } public void method2(){system.out.println( "方法2 start"); try {synchronized(this){system.out.println( "method 2 execute"); thread.sleep(1000); }} catch(arturnedexception e){e.printstacktrace(); } system.out.println( "Method 2 End"); } public static void main(string [] args){final synchronizedtest test = new synchronizedTest();新しいスレッド(new runnable(){@override public void run(){test.method1();}})。start();新しいスレッド(new runnable(){@override public void run(){test.method2();}})。start(); }}実行結果は次のとおりです。スレッド1とスレッド2の両方が対応するメソッドを入力して実行を開始しますが、スレッド2は、同期ブロックに入る前にスレッド1の同期ブロック実行が完了するのを待つ必要があります。
方法1開始
方法1実行
方法2開始
方法1エンド
方法2実行
方法2エンド
2。同期された原理
上記の実行結果についてまだ質問がある場合は、心配しないでください。まず同期の原則を理解し、次に上記の質問を振り返って一目で見てみましょう。まず、次のコードを逆コンパイルすることにより、同期コードがコードブロックをどのように同期するかを見てみましょう。
パッケージcom.paddx.test.concurrent; public class synchronizeddemo {public void method(){synchronized(this){system.out.println( "method 1 start"); }}}逆コンパイルの結果:
これら2つの指示の役割に関して、JVM仕様の説明を直接参照してください。
MonitorEnter:
各オブジェクトはモニターに関連付けられています。所有者がいる場合にのみ、モニターがロックされています。 MonitorEnterを実行するスレッドは、次のようにObjectRefに関連付けられたモニターの所有権を取得しようとします。•ObjectRefに関連付けられたモニターのエントリカウントがゼロの場合、スレッドはモニターに入り、エントリカウントを1に設定します。スレッドはモニターの所有者です。•スレッドが既にObjectRefに関連付けられているモニターを所有している場合、モニターに再び入り、エントリカウントが増加します。
この箇所の一般的な意味は次のとおりです。
各オブジェクトにはモニターロック(モニター)があります。モニターが占有されると、ロックされます。スレッドがMonitorEnter命令を実行すると、モニターの所有権を取得しようとします。プロセスは次のとおりです。
1.モニターのエントリ数が0の場合、スレッドがモニターに入り、エントリ番号を1に設定すると、スレッドはモニターの所有者です。
2.スレッドが既にモニターを所有しており、再入力するだけの場合、モニターへのエントリの数が1に追加されます。
3.他のスレッドがモニターを占有している場合、スレッドはモニターのエントリ数が0になるまでブロッキング状態に入り、モニターの所有権を再度取得しようとします。
Monitorexit:
Monitorexitを実行するスレッドは、ObjectRefが参照するインスタンスに関連付けられたモニターの所有者でなければなりません。スレッドは、ObjectRefに関連付けられたモニターのエントリカウントを減少させます。その結果、エントリカウントの値がゼロの場合、スレッドはモニターを終了し、その所有者ではなくなりました。モニターに入るためにブロックしている他のスレッドは、そうすることを許可されています。
この箇所の一般的な意味は次のとおりです。
Monitorexitを実行するスレッドは、ObjectRefに対応するモニターの所有者でなければなりません。
命令が実行されると、入力のモニターの数が1削減されます。入力の入力の数が0の場合、スレッドはモニターを終了し、このモニターの所有者ではなくなります。このモニターによってブロックされた他のスレッドは、このモニターの所有権を取得しようとすることができます。
説明のこれら2つの段落を通して、同期の実装原則を明確に確認できるはずです。同期のセマンティックの基礎となる層は、モニターオブジェクトを介して完成します。実際、待機/通知などのメソッドもモニターオブジェクトに依存しています。これが、待機/通知などのメソッドのみを同期ブロックまたはメソッドで呼び出すことができる理由です。そうしないと、java.lang.illegalMonitorStateExceptionの例外がスローされます。
同期方法の逆コンパイル結果を見てみましょう。
ソースコード:
パッケージcom.paddx.test.concurrent; public class synchronizedmethod {public synchronized void method(){system.out.println( "hello world!"); }}逆コンパイルの結果:
逆コンパイルの結果から判断すると、メソッドの同期は、MonitorEnterとMonitorexitの命令を介して完了しません(理論的には、これら2つの命令を通じて実装することもできます)。ただし、通常の方法と比較して、acc_synchronized識別子が一定のプールに追加されます。 JVMは、この識別子に基づいてメソッドの同期を実装します。メソッドが呼び出されると、通話命令はメソッドのACC_SynChronized Accessフラグが設定されているかどうかを確認します。設定した場合、実行スレッドは最初にモニターを取得し、次にメソッドが正常に実行された後にメソッド本体を実行します。メソッドが実行された後、モニターがリリースされます。メソッドの実行中、他のスレッドは同じモニターオブジェクトを取得できなくなります。実際、本質に違いはありませんが、メソッドの同期は、Bytecodeを介して実行する必要なく、それを達成する暗黙の方法です。
3。操作結果の説明
同期の原則を理解することで、上記のプログラムを見ることで簡単に解決できます。
1。コードセグメント2の結果:
Method1とMethod2は異なる方法ですが、両方の方法は同期され、同じオブジェクトを介して呼び出されます。したがって、呼び出す前に、同じオブジェクトでロック(モニター)を競う必要があるため、相互に排他的にロックを取得できます。したがって、Method1とMethod2は順次のみ実行できます。
2。コードセグメント3結果:
testとtest2は異なるオブジェクトに属しますが、Testとtest2は同じクラスの異なるインスタンスに属します。 Method1とMethod2はどちらも静的同期メソッドに属しているため、同じクラス(各クラスは1つのクラスオブジェクトにのみ対応する)でモニターを取得する必要があるため、順次しか実行できません。
3。コードセグメント4結果:
コードブロックの同期の場合、同期されたキーワードの後にブラケット内のオブジェクトのモニターを取得することが本質的に必要です。このコードのブラケットの内容はこれであり、Method1とMethod2は同じオブジェクトを介して呼び出されるため、同期ブロックを入力する前に同じオブジェクト上のロックを競う必要があるため、同期ブロックは順番にのみ実行できます。
4つの要約
Synchronizedは、Java Concurrentプログラミングでスレッドの安全性に最も一般的に使用される方法であり、使用が比較的簡単です。ただし、その原則を深く理解し、モニターロックなどの根本的な知識をある程度理解できれば、同期されたキーワードを正しく使用するのに役立ちます。また、同時性プログラミングメカニズムをよりよく理解するのに役立ち、さまざまな状況下でTASを完了するためのより良い並行戦略を選択するのに役立ちます。また、日常生活で遭遇するさまざまな同時問題に冷静に対処することもできます。