Java 5の前に、同期されたキーワードを使用してロック関数を実装しました。
同期されたキーワードは、修飾子(同期された方法)として、または関数内のステートメント(同期コードブロック)として使用できます。
同期するために、重要なのは、そのものの使用をロックとしてマスターすることです。クラスの非静的方法(メンバー方法)の場合、オブジェクトインスタンスのロックを取得することを意味します。クラスの静的方法(クラスメソッド)の場合、クラスオブジェクトのロックを取得する必要があります。同期コードブロックの場合、どのオブジェクトのロックが取得されるかを指定する必要があります。同期された非静的メソッドは、メソッド全体を含む同期(this){…}コードブロックと見なすことができます。
同期コードブロックまたは同期メソッドであっても、一度に1つのスレッドのみを入力できます(最大で1つのスレッドが同時にコードセグメントを実行します)。他のスレッドが入力しようとする場合(同じ同期ブロックまたは異なる同期ブロックであろうと)、JVMはそれらを掛けます(待機中のロックプールに入れます)。この構造は、並行性理論の重要なセクションと呼ばれます。
JVMでは、効率を向上させるために、同時に実行される各スレッドには、処理されているデータのキャッシュコピーがあります。同期のために同期を使用する場合、実際に同期されているのは、異なるスレッドのロックされたオブジェクトを表すメモリブロックです(コピーデータはメインメモリと同期したままになります。単語の同期が使用される理由がわかります)。簡単に言えば、同期ブロックまたは同期メソッドが実行された後、ロックされたオブジェクトに加えられた変更は、ロックを解放する前にメインメモリに書き戻す必要があります。同期ブロックを入力してロックを取得した後、ロックされたオブジェクトのデータはメインメモリから読み取られ、ロックを保持しているスレッドのデータコピーは、メインメモリのデータビューと同期する必要があります。
以下は、同期のさまざまな状況を説明するための具体的な例です。
同期化された同期方法
まず、同期方法の例を見てみましょう。
public class synchronizedtest1 extends thread {private synchronized void testsynchronizedmethod(){for(int i = 0; i <10; i ++){system.out.println(thread.currentthread()。 {thread.sleep(100); } catch(arturnedexception e){e.printstacktrace(); }}} @Override public void run(){testsynchronizedMethod(); } public static void main(string [] args){synchronizedtest1 t = new synchronizedTest1(); t.start(); t.testsynchronizedmethod(); }}プログラム出力の実行:
主なtestsynchronizedMethod:0 Main testsynchronizedMethod:1 Main testsynchronizedMethod:2 Main testsynchronizedMethod:3 Main testsynchronizedMethod:4 Main testsynchronizedMethod:5 Main testsynchronizedMethod:4 Main testsynchronized:7 Main testsynchronized:7 Main testSynchronized:7 testsynchronizedMethod:9スレッド-0 testsynchronizedMethod:0スレッド-0 testsynchronizedmethod:1 thread-0 testsynchronizedmethod:2 testsynchronizedmethod:3スレッド-0 testsynchronizedmethod:4 testsynchronizedmethod:5スレッド-0スレッド-0スレッド-0スレッド-0スレッドtestsynchronizedMethod:7スレッド-0 testsynchronizedmethod:8スレッド-0 testsynchronizedmethod:9
TestSynchronizedMethodメソッドは、2つのスレッド間で同期的に実行されることがわかります。
メインメソッドが次のように変更されている場合、2つのスレッドの同期モニターは同じオブジェクトではなく、同期ロールを再生できないため、2つのスレッドは同期して実行できません。
public static void main(string [] args){thread t = new synchronizedTest1(); t.start();スレッドT1 = new synchronizedTest1(); t1.start(); }出力の結果は次のとおりです。
Thread-0 testsynchronizedMethod:0スレッド1 testsynchronizedMethod:0 thread-0 testsynchronizedmethod:1 thread-1 testsynchronizedmethod:1 testr-1 testsynchronizedmethod:2 testsynchronizedmethod:2スレッド-0 testsynchronized:3スレッド1テストシンメトド: testsynchronizedMethod:4スレッド1 testsynchronizedMethod:4スレッド-0 testsynchronizedmethod:5スレッド1 testsynchronizedmethod:5 testsynchronizedmethod:6 testsynchronizedmethod:6スレッド-0 testsynchronizedmethod:7スレッド-1 testsnchronizedmethod: testsynchronizedMethod:8 Thread-1 testsynchronizedMethod:8 Thread-0 testsynchronizedMethod:9 Thread-1 testsynchronizedMethod:9
修正されたメインメソッドを2つのスレッド間で同期して実行できる場合、2つのスレッドのモニターが同じオブジェクト(クラスオブジェクト)であり、同期的に実行できるように、testsynchronizedMethodメソッドを静的メソッドとして宣言する必要があります。変更されたコードは次のようになります:
public class synchronizedtest1 extends thread {private static synchronized void testsynchronizedmethod(){for(int i = 0; i <10; i ++){system.out.println(thread.currentthread()。 {thread.sleep(100); } catch(arturnedexception e){e.printstacktrace(); }}} @Override public void run(){testsynchronizedMethod(); } public static void main(string [] args){thread t = new synchronizedTest1(); t.start();スレッドT1 = new synchronizedTest1(); t1.start(); }}出力の結果は次のとおりです。
スレッド-0 testsynchronizedMethod:0スレッド-0 testsynchronizedmethod:1スレッド-0 testsynchronizedmethod:2 thread-0 testsynchronizedmethod:3 testsynchronizedmethod:4 thread-0 testsynchronizedmethod:5スレッド-0 testsynizedmethod:7 swart-0 testsynchmmet testsynchronizedMethod:8スレッド-0 testsynchronizedmethod:9スレッド1 testsynchronizedmethod:0 thread-1 testsynchronizedmethod:1 testsynchronizedmethod:2 thread-1 testsynchronizedmethod:3 thread-1 testsynchronizedmethod:4 testsynchronizedmethod:5スレッド-1 testsynchronizedmethod: testsynchronizedMethod:7スレッド1 testsynchronizedmethod:8スレッド1 testsynchronizedmethod:9
同期ブロックの状況は、同期ブロックが同期制御の粒度を低下させることを除いて、同期法に似ています。
このオブジェクトを使用して、同じオブジェクトインスタンス間の同期を制御します。
public class synchronizedtest2 extends thread {private void testsynchronizedblock(){synchronized(this){for(int i = 0; i <10; i ++){system.out.println(thread.currentthread()。 {thread.sleep(100); } catch(arturnedexception e){e.printstacktrace(); }}}} @Override public void run(){testsynchronizedBlock(); } public static void main(string [] args){synchronizedtest2 t = new synchronizedTest2(); t.start(); t.testsynchronizedblock(); }}出力結果:
メインTestSynchronizedBlock:0 Main testSynchronizedBlock:1 Main testsynchronizedBlock:2 Main testsynchronizedBlock:3 Main testsynchronizedBlock:4 Main testsynchronizedBlock:5 Main testsynchronizedBlock:6 Main testsynchronizedBlock:7 Main testsynchronizedBlock:9 Sprew-Block:8 Main testsynizedblock: testsynchronizedBlock:0スレッド-0 testsynchronizedblock:1スレッド-0 testsynchronizedblock:2スレッド-0 testsynchronizedblock:3 testsynchronizedblock:4 testsynchronizedblock:5スレッド-0 testsynchronizedblock:6スレッド-0 testsynchronizedblock:7スレッドtestSynchronizedBlock:9
クラスオブジェクトを使用して、異なるインスタンス間の同期を制御します。
public class synchronizedtest2 extends thread {private void testsynchronizedblock(){synchronizedtest2.class){for(int i = 0; i <10; i ++){system.out.println(thread.currentthread()。 {thread.sleep(100); } catch(arturnedexception e){e.printstacktrace(); }}}} @Override public void run(){testsynchronizedBlock(); } public static void main(string [] args){thread t = new synchronizedTest2(); t.start();スレッドT2 = new synchronizedTest2(); t2.start(); }}出力結果:
スレッド-0 testsynchronizedblock:0スレッド-0 testsynchronizedblock:1スレッド-0 testsynchronizedblock:2 thread-0 testsynchronizedblock:3 testsynchronizedblock:4 testsynchronizedblock:5スレッド-0 testsynchronizedblock:6スレッド-0 testsynchronizedブロック:スレッド-0 testsynchronizedblock:9スレッド1 testsynchronizedblock:0スレッド1 testsynchronizedblock:1 tesch-1 testsynchronizedblock:2スレッド1 testsynchronizedblock:3スレッド1 testsynchronizedblock:4スレッド1 testsynchronizedblock: testsynchronizedBlock:8スレッド1 testsynchronizedblock:9
同期制御に同期キーワードを使用する場合、オブジェクトモニターを把握する必要があります。モニターを取得するプロセスのみが実行される可能性があり、他のすべてはモニターを取得するために待機する必要があります。非ヌルオブジェクトは、オブジェクトモニターとして使用できます。同期された方法がメソッドに作用すると、オブジェクトインスタンスがロックされます。静的メソッドに作用する場合、オブジェクトインスタンスはオブジェクトに対応してロックされます。
2つのスレッドがオブジェクトに同時にアクセスする同期方法
2つの同時スレッドが同じオブジェクトの同期メソッドにアクセスすると、1つのスレッドのみを実行できます。別のスレッドは、現在のスレッドが実行される前にこれを実行するのを待つ必要があります。
public class twothread {public static void main(string [] args){final twothread twothread = new Twothread();スレッドT1 = newスレッド(new runnable(){public void run(){twothread.syncmethod();}}、 "a");スレッドt2 = newスレッド(new runnable(){public void run(){twothread.syncmethod();}}、 "b"); t1.start(); t2.start(); } public synchronized void syncmethod(){for(int i = 0; i <5; i ++){system.out.println(thread.currentthread()。getName() + ":" + i); {thread.sleep(500)を試してください。 } catch(arternedexception ie){}}}}出力結果:
A:0a:1a:2a:3a:4b:0b:1b:2b:3b:4
2つのオブジェクトの同期方法は、2つのスレッドでアクセスされます
この場合、通常の方法と同じように、同期は機能しません。対応するロックはそれぞれのオブジェクトだからです。
public class two object {public static void main(string [] args){final two object object1 = new TwoObject();スレッドT1 = newスレッド(new runnable(){public void run(){object1.syncmethod();}}、 "object1"); t1.start(); final twoObject object2 = new TwoObject();スレッドT2 = newスレッド(new runnable(){public void run(){public void run(){object2.syncmethod();}}、 "object2"); t2.start(); } public synchronized void syncmethod(){for(int i = 0; i <5; i ++){system.out.println(thread.currentthread()。getName() + ":" + i); {thread.sleep(500)を試してください。 } catch(arternedexception ie){}}}}可能な出力の1つ:
Object2:0Object1:0Object1:1Object2:1Object2:2Object1:2Object2:3Object1:3Object1:4object2:4
2つのスレッドは、同期された静的メソッドにアクセスします
この場合、クラスはいつでもロックされているため、静的メソッドを実行できるスレッドは1つだけです。
同期方法と非同期メソッドに同時にアクセスすると、1つのスレッドがオブジェクトの1つの同期方法にアクセスすると、別のスレッドはそのオブジェクトの非同期メソッドにアクセスできます。
public class syncandnosync {public static void main(string [] args){final syncandnosync syncandnosync = new syncandnosync();スレッドt1 = newスレッド(new runnable(){public void run(){syncandnosync.syncmethod();}}、 "a"); t1.start();スレッドT2 = newスレッド(new runnable(){public void run(){syncandnosync.nosyncmethod();}}、 "b"); t2.start(); } public synchronized void syncmethod(){for(int i = 0; i <5; i ++){system.out.println(thread.currentthread()。getname() + "at syncmethod():" + i); {thread.sleep(500)を試してください。 } catch(arternedexception ie){}}} public void nosyncmethod(){for(int i = 0; i <5; i ++){system.out.println(thread.currentthread()。getname() + "at nosyncmethod():" + i); {thread.sleep(500)を試してください。 } catch(arternedexception ie){}}}}可能な1つの出力:
b at nosyncmethod():0a at syncmethod():0b at nosyncmethod():1a at syncmethod():1b at nosyncmethod():2a at syncmethod():2b at nosyncmethod():3a at syncmethod():3a at syncmethod():3a at syncmethod():3a nosyncmethod():4
同じオブジェクトにアクセスするための異なる同期方法
スレッドがオブジェクトの同期メソッドAにアクセスすると、オブジェクト内の他のすべての同期メソッドへの他のスレッドアクセスがブロックされます。最初のスレッドがオブジェクトロックを取得し、他のスレッドはロックを取得できないため、異なる方法にアクセスしていますが、ロックを取得せず、アクセスできません。
public class twosyncmethod {public static void main(string [] args){final twosyncmethod twosyncmethod = new TwosyncMethod();スレッドT1 = newスレッド(new runnable(){public void run(){twosyncmethod.syncmethod1();}}、 "a"); t1.start();スレッドt2 = newスレッド(new runnable(){public void run(){twosyncmethod.syncmethod2();}}、 "b"); t2.start(); } public synchronized void syncmethod1(){for(int i = 0; i <5; i ++){system.out.println(thread.currentthread()。getname() + "at syncmethod1():" + i); {thread.sleep(500)を試してください。 } catch(arternedexception ie){}}} public synchronized void syncmethod2(){for(int i = 0; i <5; i ++){system.out.println(shood.currentthread()。 {thread.sleep(500)を試してください。 } catch(arternedexception ie){}}}}出力結果:
at syncmethod1():0a at syncmethod1():1a at syncmethod1():2a at syncmethod1():3a at syncmethod1():4b at syncmethod2():0b at syncmethod2():1b at syncmethod2():2b at syncmethod2() syncmethod2():4