Java 5以来、java.util.concurrent.locksパッケージにはいくつかのロック実装が含まれているため、独自のロックを実装する必要はありません。しかし、これらのロックの使用方法をまだ理解する必要があります。
シンプルなロック
Javaの同期ブロックから始めましょう:
パブリッククラスカウンター{private int count = 0; public int inc(){synchronized(this){return ++ count; }}}INC()メソッドに同期(この)コードブロックがあることがわかります。このコードブロックでは、1つのスレッドのみがreturn ++カウントを同時に実行できるようにします。同期化された同期ブロックのコードはより複雑になる可能性がありますが、++カウントの単純な操作は、スレッドの同期の意味を表現するのに十分です。
次のカウンタークラスは、同期する代わりにロックを使用して同じ目標を達成します。
パブリッククラスカウンター{プライベートロックロック= new Lock(); private int count = 0; public int inc(){lock.lock(); int newcount = ++ count; lock.unlock(); NewCountを返します。 }}lock()メソッドはロックインスタンスオブジェクトをロックするため、オブジェクトのロック()メソッドを呼び出すすべてのスレッドは、ロックオブジェクトのロック()メソッドが呼び出されるまでブロックされます。
ロッククラスの簡単な実装です。
パブリッククラスカウンター{public class lock {private boolean islocked = false; public synchronized void lock()throws arturtedexception {while(islocked){wait(); } iSlocked = true; } public synchronized void lock(){islocked = false; notify(); }}「スピンロック」とも呼ばれるwhile(islocked)ループに注意してください。 Islockedが真である場合、スレッド呼び出しlock()がブロックされ、wait()呼び出しを待ちます。通話を受信せずにスレッドがwait()を返すのを防ぐために(誤ったウェイクアップとも呼ばれます)、スレッドはアイスロックされた状態を再確認して、目覚めた後も安全に実行し続けることができると考えるのではなく、安全に実行し続けるか、再び待機を続ける必要があるかどうかを判断します。 Islockedがfalseの場合、現在のスレッドはwhile(islocked)ループを終了し、iSlockedをtrueに戻し、ロック()メソッドを呼び出す他のスレッドがロックインスタンスにロックを追加できるようにします。
スレッドがクリティカルセクション(lock()とlock()の間にある)のコードを完了すると、unlock()が呼び出されます。 Unlock()の実行は、iSlockedにfalseに再セットされ、Lock()メソッドのWait()関数と呼ばれ、待機状態にあるスレッドの1つを通知(ウェイクアップ)します。
ロックの繰り返し可能性
Javaの同期同期ブロックはリエントラントです。これは、Javaスレッドがコード内の同期同期ブロックに入力し、同期ブロックで使用される同期オブジェクトに対応するパイプのロックを取得する場合、スレッドは同じパイプラインオブジェクトによって同期された別のJavaコードブロックを入力できることを意味します。これが例です:
public class reentrant {public synchronized outer(){inern(); } public synchronized inenter(){//何か}}}外側()と内側()の両方が同期されていると宣言されていることに注意してください。これは、Javaの同期(この)ブロックに相当します。スレッドがouter()を呼び出す場合、両方の方法(コードブロック)が同じ管理オブジェクト( "this")によって同期されるため、outer()に内側()を呼び出す問題はありません。スレッドにパイプオブジェクトにロックが既にある場合、パイプオブジェクトによって同期されたすべてのコードブロックにアクセスできます。これはリエントラントです。スレッドは、すでに持っているロックによって同期されたコードのブロックを入力できます。
上記のロックの実装は、リエントラントではありません。次のようにリエントラントクラスを書き直すと、スレッドがouter()を呼び出すと、内側()メソッドのlock.lock()でブロックされます。
public class reentrant2 {lock lock = new lock(); public outer(){lock.lock(); inner(); lock.unlock(); } public synchronized inner(){lock.lock(); //何かlock.unlock()を実行します; }}スレッドを呼び出すouter()は、最初にロックインスタンスをロックし、次に内側()を呼び出し続けます。内側()メソッドでは、スレッドはロックインスタンスを再度ロックしようとし、アクションが失敗します(つまり、スレッドはブロックされます)。
Unlock()がLock()の間で2回呼び出されない場合、ロックの2回目の呼び出しがブロックされます。 lock()の実装を見た後、理由は明らかであることがわかります。
public class lock {boolean islocked = false; public synchronized void lock()throws arturtedexception {while(islocked){wait(); } iSlocked = true; } ...}スレッドがロック()メソッドを終了できるかどうかは、whileループ(スピンロック)の条件によって決定されます。現在の判断条件は、どのスレッドがロックされるかを考慮せずに、Islockedが誤っている場合にのみロック操作が許可されることです。
このロッククラスを再発送できるようにするには、少し変更する必要があります。
public class lock {boolean islocked = false;スレッドロックby = null; int lockedCount = 0; public synchronized void lock()throws arturnedexception {thread callingthread = thread.currentthread(); while(islocked && lockedby!= callingthread){wait(); } iSlocked = true; LockedCount ++; lockedby = callingthread; } public synchronized void lock(){if(thread.currentthread()== this.lockedby){lockedCount--; if(lockedcount == 0){islocked = false; notify(); }}} ...}while loop(Spin Lock)は、ロックインスタンスをロックしたスレッドも考慮していることに注意してください。現在のロックオブジェクトがロックされていない場合(iSlocked = false)、または現在の呼び出しスレッドがロックインスタンスをロックした場合、whileループは実行されず、スレッド呼び出しロック()はメソッドを終了できます(翻訳者の注:「現在のセマンティクスでは「メソッド」と呼ばれないことを意味します)。
さらに、同じスレッドがロックオブジェクトを繰り返しロックする回数を記録する必要があります。それ以外の場合、電流ロックが何度もロックされていても、1つのUnblock()コールはロック全体のブロックを解除します。 Unlock()コールが対応するロック()呼び出しが呼び出される回数に到達するまで、ロックをロック解除したくありません。
これで、このロッククラスはリエントラントです。
ロックの公平性
Javaの同期ブロックは、スレッドがそれらを入力しようとする順序を保証するものではありません。したがって、複数のスレッドが同じ同期化された同期ブロックにアクセスするために競合し続ける場合、1つ以上のスレッドがアクセスできないというリスクがあります。つまり、アクセスは常に他のスレッドに割り当てられます。この状況はスレッドハンガーと呼ばれます。この問題を回避するには、ロックは公平性を達成する必要があります。この記事に示されているロックは、同期化された同期ブロックで内部的に実装されているため、公平であることは保証されていません。
最終的にステートメントでunlock()を呼び出します
重要な領域を保護するためにロックが使用され、重要な領域が例外を投げかける可能性がある場合、最終的なステートメントでUnlock()を呼び出すことが非常に重要です。これにより、ロックオブジェクトのロックが解除され、他のスレッドがロックされ続けることができます。これが例です:
lock.lock(); try {//クリティカルセクションコードを実行します。この単純な構造により、臨界領域に例外がスローされたときにロックオブジェクトがロック解除されることが保証されます。 Unlock()が最終的なステートメントで呼び出されない場合、例外がクリティカルセクションにスローされた場合、ロックオブジェクトはロックされた状態にとどまり、ロックオブジェクトのロック()を呼び出す他のすべてのスレッドがブロックされます。
上記は、Javaマルチスレッドロックに関する情報です。今後も関連情報を追加し続けます。このサイトへのご支援ありがとうございます!