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
- 関連項目:
ReentrantLock
,Condition
,ReadWriteLock
-
メソッドの詳細
-
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
実装が状態をサポートしていない場合
-