システム内の複数のジョブが、同じオブジェクトにアクセスし、操作する必要がある場合があります。 ILock は、共用オブジェクトへの排他的アクセスを認可するプロトコルを定義します。 共用オブジェクトにアクセスする必要がある場合、ジョブがそのオブジェクトのロックを獲得 します。 オブジェクトの操作が完了すると、ジョブはロックを解放 します。
通常、ロックは、共用オブジェクトが作成されたときか、共用オブジェクトにプラグインが最初にアクセスしたときに作成されます。 つまり、共用オブジェクトへの参照を持つコードも、ロックへの参照を持ちます。 ここではまず、myObject へのアクセスを制御するロック myLock を作成するところから始めます。
... myObject = initializeImportantObject(); IJobManager jobMan = Platform.getJobManager(); myLock = jobMan.newLock(); ...
プラットフォームによって、ILock の 堅固な実装が提供されています。 ジョブ・マネージャーは、クライアントが使用するこのロックのインスタンスを提供します。 これらのロックは相互で認識し、循環デッドロックを回避します。(このステートメントについての詳細は、後で説明します。)
ジョブのコードが myObject へのアクセスを要求する場合、このコードは、最初にこのオブジェクトに対するロックを取得する必要があります。 以下のコードの断片は、ロックを操作する共通イディオムを示しています。
... // I need to manipulate myObject, so I get its lock first. try { myLock.acquire(); updateState(myObject); // manipulate the object } finally { lock.release(); } ...
acquire() メソッドは、呼び出しジョブにロックへの排他的アクセスが認可されるまで戻りません。 つまり、他のジョブが既にロックを獲得している場合、このコードはロックが使用可能になるまでブロックされます。 ロックを獲得し、myObject を操作するコードは、try ブロックにラップされるため、 オブジェクトの操作時に例外が発生した場合は、ロックが解放されることに注意してください。
これは単純であるように見えます。 ロックの使用は、非常に簡単です。 また、ロックは再入可能です。つまり、ジョブが同じロックを複数回獲得することはありません。 各ロックは、特定のスレッドに対する獲得と解放の回数を保持し、リリース回数が獲得回数と同じになった場合にジョブから解放されます。
ジョブ・マネージャーによって提供されたロックは、相互で認識し、循環デッドロックを回避することは説明しました。 デッドロックの発生する仕組みを理解するために、単純なシナリオを考えてみます。 「Job A」が「Lock A」を獲得し、その後、「Lock B」の獲得を試行すると想定します。 一方、「Lock B」は、現在「Lock A」を待機してブロックされている「Job B」に保持されています。 この種類のデッドロックは、ジョブ間でロックを使用する場合の基本的な設計の問題を示しています。 この単純なケースではデッドロックを回避するのは簡単です。 設計で使用されるジョブとロックの数が増加するに従って、偶然にデッドロックが発生する可能性は上がります。
プラットフォームでデッドロックを識別できるということが、この場合助けになります。 デッドロック状態を検出すると、ジョブ・マネージャーはデッドロック状態を説明する診断情報をログに記録します。 その後、ブロックされたジョブが所有するロックへのアクセス権をこのロックを待機している他のジョブに一時的に認可して、デッドロックを解消します。 複数のロックを含む実装を十分にテストし、プラットフォームによって報告されたデッドロック状態を修正することが重要です。