#1. 同期ロックの再入可能性
1.1 はじめに
キーワード synchronized にはロックの再入機能があります。つまり、synchronized を使用すると、スレッドがオブジェクト ロックを取得したときに、再度オブジェクト ロックを要求したときに、再度オブジェクト ロックを取得できます。これは、同期メソッド/ブロック内でこのクラスの他の同期メソッド/ブロックを呼び出すときに、常にロックを取得できることを示しています。例:
public class Service1 { public synchronized void method1(){ System.out.println("method1"); method2(); } public synchronized void method2(){ System.out.println("method2"); method3(); } public synchronized void method3(){ System.out.println("method3"); } }
public class MyThread extends Thread { @Override public void run(){ Service1 service1 = new Service1(); service1.method1(); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }
☹ この結果を見たとき、私はこう思いました。混乱しています、なぜですか?
リエントラント ロックであることが証明されましたか? ➤ 「リエントラントロック」の概念は、自分自身の内部ロックを再度取得できるということです。たとえば、スレッドがオブジェクトのロックを取得します。この時点では、オブジェクトのロックは解放されていません。再度取得します。このオブジェクトのロックを取得することは可能です。ロックが再入可能でない場合は、デッドロックが発生します。
➤ 「リエントラント ロック」の最大の役割は、
デッドロックを回避することです
##1.2 分析当社はそれを知っています。プログラムでは、同期モニターのロックを明示的に解放することはできませんが、次の場合にロックが解放されます。
① 現在のスレッドの同期メソッドおよびコード ブロックの実行が終了したときに解放されます。 ② ときに解放されます。同期メソッドまたは同期コード ブロックがコード ブロックまたはメソッドを終了すると、現在のスレッドがブレークまたはリターンに遭遇します。
③ 未処理のエラーまたは例外が発生して異常終了が発生すると解放されます。
④ プログラムは同期オブジェクトの wait メソッドを実行します。現在のスレッドは一時停止し、ロックを解放します。
したがって、上記のプログラムでは、スレッドが同期メソッドメソッド 1 に入ると、サービス 1 のオブジェクト ロックを取得しますが、メソッド 1 を実行すると、同期メソッドメソッド 2 が呼び出されます。通常の場合、同期メソッドmethod2を実行する際にはオブジェクトロックも取得する必要がありますが、上記のロック解除条件によれば、この時点ではmethod1のオブジェクトロックが解除されていないため、デッドロックが発生し、method2 は実行を続行できなくなります。ただし、上記のコードの実行結果から判断すると、method2 とmethod3 は正常に実行できます。つまり、
を取得できます。
1.3 親子継承性親子クラス継承環境ではリエントラントロックがサポートされており、サンプルコードは以下の通りです。
public class Service2 { public int i = 10; public synchronized void mainMethod(){ i--; System.out.println("main print i="+i); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class Service3 extends Service2 { public synchronized void subMethod(){ try{ while (i>0){ i--; System.out.println("sub print i= "+i); Thread.sleep(100); this.mainMethod(); } }catch (InterruptedException e){ e.printStackTrace(); } } }
public class MyThread extends Thread { @Override public void run(){ Service3 service3 = new Service3(); service3.subMethod(); } public static void main(String[] args) { MyThread myThread = new MyThread(); myThread.start(); } }
実行結果は次のとおりです:
このプログラムは、親クラスと子クラスの間に継承関係がある場合、サブクラスは親の同期メソッドを完全に呼び出すことができることを示しています。 「リエントラントロック」によるクラス。
2. 例外が発生すると、ロックは自動的に解放されます。スレッドによって実行されるコードで例外が発生すると、ロックは自動的に解放されます。保持すると自動的に解放され、解放されます。
検証コードは次のとおりです。public class Service4 { public synchronized void testMethod(){ if(Thread.currentThread().getName().equals("a")){ System.out.println("ThreadName= "+Thread.currentThread().getName()+" run beginTime="+System.currentTimeMillis()); int i=1; while (i == 1){ if((""+Math.random()).substring(0,8).equals("0.123456")){ System.out.println("ThreadName= "+Thread.currentThread().getName()+" run exceptionTime="+System.currentTimeMillis()); //Integer.parseInt("a"); } } }else{ System.out.println("Thread B run time= "+System.currentTimeMillis()); } } }
public class ThreadA extends Thread{ private Service4 service4; public ThreadA(Service4 service4){ this.service4 = service4; } @Override public void run(){ service4.testMethod(); } }
public class ThreadB extends Thread{ private Service4 service4; public ThreadB(Service4 service4){ this.service4 = service4; } @Override public void run(){ service4.testMethod(); } }
public class Main { public static void main(String[] args) { try { Service4 service4 = new Service4(); ThreadA a = new ThreadA(service4); a.setName("a"); a.start(); Thread.sleep(500); ThreadB b = new ThreadB(service4); b.setName("b"); b.start(); } catch (InterruptedException e) { e.printStackTrace(); } } }
は、現時点では注釈付きの状態にあり、実行中の結果は次のとおりです。
スレッド a にはエラーがないため、while(true)、スレッド a はこの時点で無限ループにあり、ロックは常に a によって占有されており、スレッド b はロック、つまりスレッド b は実行できません。
Service4 クラスの
のコメントを解除すると、実行結果は次のようになります。
スレッド a でエラーが発生すると、スレッド b がロックを取得して実行しますが、メソッド内で例外が発生すると自動的にロックが解放されることがわかります。
3. 任意のオブジェクトをモニターとして使用する#java は、「任意のオブジェクト」を「オブジェクト モニター」として同期する機能をサポートしています。これらの「任意のオブジェクト」のほとんどはインスタンス変数とメソッド パラメーターであり、形式は同期された (このオブジェクト x ではない) 同期されたコード ブロックです。
サンプル コードは次のとおりです:public class StringLock { private String lock = "lock"; public void method(){ synchronized (lock){ try { System.out.println("当前线程: "+Thread.currentThread().getName() + "开始"); Thread.sleep(1000); System.out.println("当前线程: "+Thread.currentThread().getName() + "结束"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { final StringLock stringLock = new StringLock(); new Thread(new Runnable() { @Override public void run() { stringLock.method(); } },"t1").start(); new Thread(new Runnable() { @Override public void run() { stringLock.method(); } },"t2").start(); } }
このオブジェクト以外のオブジェクトをロックすると、特定の利点があります。クラス、同期は達成できますが、ブロックされるため、操作効率に影響します。ただし、同期コード ブロックを使用して this 以外のオブジェクトをロックすると、同期 (this 以外) コード内のプログラムと同期メソッドがブロックは非同期であり、他のロックの同期方法は許可されていません。このロックを争うことにより、操作効率が大幅に向上します。
4. 同期には継承がありません
#親クラスの同期メソッドは、同期を追加せずにサブクラスで書き換えられると機能しません。同期なので、サブクラスのメソッドに synchronized キーワードを追加する必要があります。
推奨学習: Java ビデオ チュートリアル
以上が同期の4つの特徴を見てみましょうの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。