Lock実装は、synchronizedのメソッドや文を使用することで取得可能なロック操作よりも広範なロック操作を提供します。 この実装を使用すると、より柔軟な構築を行ったり、まったく異なるプロパティを保持したり、関連する複数のConditionオブジェクトをサポートしたりできるようになります。
ロックは、複数のスレッドによる共有リソースへのアクセスを制御するためのツールです。 通常、ロックは共有リソースへの排他的なアクセスを提供します。ロックを取得できるのは一度に1つのスレッドだけであり、すべての共有リソースにアクセスするにはロックを最初に取得する必要があります。 ただし、ReadWriteLockの読込みロックなどの一部のロックでは、共有リソースへの並行アクセスが許可される場合があります。
synchronizedメソッドまたは文の使用により、すべてのオブジェクトに関連付けられた暗黙の監視ロックへのアクセスが提供されますが、ロックの取得と解放のすべてをブロック構造に従って行うことが求められます。このため、複数のロックが取得された場合、その解放は取得とは反対の順序で行う必要があります。また、すべてのロックの解放は、それらが取得された範囲内で行う必要があります。
synchronizedメソッドおよび文の範囲メカニズムによって、監視ロックのプログラミングがはるかに容易になり、ロックに関連した一般的なプログラミング・エラーの多くが回避しやすくなる一方で、ロックをより柔軟な方法で操作することが必要な状況も発生します。 For example, some algorithms for traversing concurrently accessed data structures require the use of "hand-over-hand" or "chain locking":この場合、ノードAのロックを取得し、次にノードBのロックを取得し、次にAを解放してCを取得し、次にBを解放してDを取得する、という具合に処理を進めます。 Lockインタフェースの実装により、ロックを異なるスコープ内で取得および解放したり、複数のロックを任意の順序で取得および解放したりできるようにすることで、このようなテクニックの使用が可能になります。
このように柔軟性が高まると、新たな責任も発生します。 ブロック構造ロックが存在しなくなることで、synchronizedメソッドおよび文で実行されるロックの自動解放が機能しなくなります。 たいていの場合、次のコードを使用する必要があります。
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
ロックおよびロック解除が異なるスコープ内で行われる場合、ロックの保持中に実行されるすべてのコードがtry-finallyまたはtry-catchにより保護され、必要に応じてロックが確実に解放されるように注意を払う必要があります。
Lock実装は、ロックを取得するための非ブロック試行(tryLock())、割込み可能なロックを取得するための試行(lockInterruptibly())、およびタイム・アウト可能なロックを取得するための試行(tryLock(long, TimeUnit))を提供することによって、synchronizedメソッドおよび文の使用に対する追加機能を提供します。
Lockクラスは、保証された順序付け、再入不可能な使用、デッドロックの検出など、暗黙の監視ロックとはまったく異なる動作やセマンティックスを提供できます。 実装がこうした特殊セマンティックスを提供する場合、実装はこれらのセマンティックスをドキュメント化する必要があります。
Lockインスタンスは、通常のオブジェクトであるため、それ自体をsynchronized文のターゲットとして使用できることに注意してください。 Lockインスタンスの監視ロックの取得と、そのインスタンスのいずれかのlock()メソッドの呼び出しとの間に指定された関係はありません。 混乱を避けるために、独自の実装内で行う場合を除き、Lockインスタンスをこの方法では決して使用しないようにすることをお薦めします。
特に記載がないかぎり、パラメータにnull値を渡すとNullPointerExceptionがスローされます。
メモリーの同期化
Lockのすべての実装で、Java言語仕様の第17章 で説明されているとおり、組込みのモニター・ロックで提供されているものと同じメモリー同期セマンティクスを強制する必要があります:
- 成功した
lock操作には、成功したLockアクションと同じメモリー同期効果がある。 - 成功した
unlock操作には、成功したUnlockアクションと同じメモリー同期効果がある。
実装上の考慮事項
ロック取得の3つの形式(割込み可、割込み不可、および時間指定)では、パフォーマンス特性、順序付けの保証、ほかの実装品質が異なります。 さらに、進行中のロック取得への割込み機能も、特定のLockクラスでは使用できない可能性があります。 このため、3つのロック取得形式すべてで、実装が厳密に同じ保証やセマンティックスを定義する必要はありません。また、進行中のロック取得の割込みをサポートする必要もありません。 実装は、各ロック・メソッドの提供するセマンティックスおよび保証を明確にドキュメント化する必要があります。 また、ロック取得の割込みがサポートされる範囲内(全体またはメソッド・エントリのみ)で、このインタフェースで定義された割込みセマンティックスに従う必要もあります。
通常、割込みは取消しを意味し、割り込みのチェックは頻繁に行われるものではないため、実装は通常のメソッド復帰に対する割り込みに肯定的に応答できます。 これは、別のアクションがスレッドをブロック解除したあとに、割込みが発生したことが示される場合にも当てはまります。 実装は、この動作をドキュメント化する必要があります。
- Java言語仕様を参照してください:
-
17.4 メモリー・モデル
- 導入されたバージョン:
- 1.5
- 関連項目:
-
メソッドの詳細
-
lock
void lock()ロックを取得します。ロックを使用できない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、ロックが取得されるまで待機します。
実装上の考慮事項
Lock実装は、デッドロックを引き起こす呼び出しなどのロックの不正使用を検出し、そのような状況で(チェックされない)例外をスローできます。Lock実装により、該当する状況および例外の型をドキュメント化する必要があります。 -
lockInterruptibly
void lockInterruptibly() throws InterruptedException現在のスレッドに対して割り込みが発生していないかぎり、ロックを取得します。利用可能な場合にロックを取得して、すぐに復帰します。
ロックを使用できない場合、現在のスレッドはスレッドのスケジューリングに関して無効になり、次の2つのいずれかが起きるまで待機します。
- 現在のスレッドによりロックが取得される。
- ほかのいずれかのスレッドが現在のスレッドに割り込みを行い、かつロック取得の割込みがサポートされている。
現在のスレッドで、
- このメソッドへのエントリ上で設定された割込みステータスが保持されるか、
- ロックの取得およびロック取得の中断がサポートされている状況で、割り込みが発生する場合、
InterruptedExceptionがスローされ、現在のスレッドの割込みステータスがクリアされます。実装上の考慮事項
ある状況下ではロック取得に割り込むことができず、場合によっては負荷の大きい操作になる場合があります。 プログラマは、このような場合があることを意識しておく必要があります。 実装は、該当する状況をドキュメント化する必要があります。
実装は、通常のメソッド復帰に対する割り込みに肯定的に応答できます。
Lock実装は、デッドロックを引き起こす呼び出しなどのロックの不正使用を検出し、そのような状況で(チェックされない)例外をスローできます。Lock実装により、該当する状況および例外の型をドキュメント化する必要があります。- スロー:
InterruptedException- 現在のスレッドがロックの取得中に割り込まれた(かつ、ロック取得の割込みがサポートされている)場合
-
tryLock
boolean tryLock()呼出し時にロックされていない場合にのみ、ロックを取得します。使用可能な場合はロックを取得し、ただちに値
trueで復帰します。 ロックが使用できない場合、このメソッドはただちに値falseで復帰します。このメソッドの通常の使用方法を次に示します。
この使用により、ロックが取得された場合にロックが解除されること、およびロックが取得されなかった場合にロック解除を試みないことが保証されます。Lock lock = ...; if (lock.tryLock()) { try { // manipulate protected state } finally { lock.unlock(); } } else { // perform alternative actions }- 戻り値:
- ロックが取得された場合は
true。それ以外の場合はfalse
-
tryLock
boolean tryLock(long time, TimeUnit unit) throws InterruptedException 指定された待機時間内でロックが利用可能であり、現在のスレッドで割り込みが発生していない場合に、ロックを取得します。ロックが使用可能な場合、このメソッドはただちに値
trueで復帰します。 ロックが利用できない場合、現在のスレッドがスレッド・スケジューリングに関して無効になり、次の3つのいずれかが起きるまで待機します。- 現在のスレッドによりロックが取得される。
- ほかのいずれかのスレッドが現在のスレッドに割り込みを行い、かつロック取得の割込みがサポートされている。または
- 指定された待機時間が経過する。
ロックが取得された場合は、値
trueが返されます。現在のスレッドで、
- このメソッドへのエントリ上で設定された割込みステータスが保持されるか、
- ロックの取得およびロック取得の中断がサポートされている状況で、割り込みが発生する場合、
InterruptedExceptionがスローされ、現在のスレッドの割込みステータスがクリアされます。指定された待機時間が経過すると、値
falseが返されます。 時間がゼロまたはそれより小さい場合、メソッドは待機しません。実装上の考慮事項
ある状況下ではロック取得に割り込むことができず、場合によっては負荷の大きい操作になる場合があります。 プログラマは、このような場合があることを意識しておく必要があります。 実装は、該当する状況をドキュメント化する必要があります。
実装は、通常のメソッド復帰に対する割り込み、またはタイム・アウトのレポートに肯定的に応答できます。
Lock実装は、デッドロックを引き起こす呼び出しなどのロックの不正使用を検出し、そのような状況で(チェックされない)例外をスローできます。Lock実装により、該当する状況および例外の型をドキュメント化する必要があります。- パラメータ:
time- ロックの最長待機時間unit-time引数の時間単位- 戻り値:
- ロックが取得された場合は
true。ロックが取得される前に待機時間が経過した場合はfalse - スロー:
InterruptedException- 現在のスレッドがロックの取得中に割り込まれた(かつ、ロック取得の割込みがサポートされている)場合
-
unlock
void unlock()このロックを解除します。実装上の考慮事項
通常、
Lock実装は、ロックを解放可能なスレッドに関して制限を課し(一般に、ロックのホルダーだけが解放できる)、制限が侵された場合には(チェックされない)例外をスローできます。Lock実装により、すべての制限および例外の型をドキュメント化する必要があります。 -
newCondition
Condition newCondition()このLockインスタンスにバインドされた新しいConditionインスタンスを返します。状態の待機を実行する前に、現在のスレッドがロックを保持する必要があります。
Condition.await()への呼出しにより、待機の前にロックを原子的に解放し、待機が復帰する前にロックを再取得します。実装上の考慮事項
Conditionインスタンスの正確な操作はLock実装に依存するため、その実装によってドキュメント化される必要があります。- 戻り値:
- この
Lockインスタンス用の新規Conditionインスタンス - スロー:
UnsupportedOperationException- このLock実装が状態をサポートしていない場合
-