モジュール java.base

クラスStampedLock

java.lang.Object
java.util.concurrent.locks.StampedLock
すべての実装されたインタフェース:
Serializable

public class StampedLock extends Object implements Serializable
読取り/書込みアクセスを制御する3つのモードを持つ機能ベースのロックです。 StampedLockの状態はバージョンとモードから構成されます。 ロック取得メソッドは、ロック状態に対するアクセスを表現および制御するスタンプを返します。これらのメソッドの「try」バージョンは、アクセスの取得失敗を表すために特殊な値ゼロを代わりに返す場合があります。 ロック解放メソッドとロック変換メソッドは、引数としてスタンプを必要とし、それらがロックの状態と一致しない場合は失敗します。 モードは次の3つです。
  • 書込み。 writeLock()メソッドは、排他的アクセスを待機してブロックする可能性があり、ロックを解放するためにunlockWrite(long)メソッドで使用できるスタンプを返します。 時間指定のないバージョンと時間指定のあるバージョンのtryWriteLockも用意されています。 ロックが書込みモードで保持されているときは、読取りロックを取得することはできず、オプティミスティック読取り検証はすべて失敗します。
  • 読取り。 readLock()メソッドは、非排他的アクセスを待機してブロックする可能性があり、ロックを解放するためにunlockRead(long)メソッドで使用できるスタンプを返します。 時間指定のないバージョンと時間指定のあるバージョンのtryReadLockも用意されています。
  • オプティミスティック読取り。 tryOptimisticRead()メソッドは、現在ロックが書込みモードで保持されていない場合のみ、ゼロ以外のスタンプを返します。 メソッドvalidate(long)は、指定のスタンプが取得されてからロックが書込みモードで取得されなかった場合にtrueを返します。この場合、tryOptimisticReadへの呼び出しのあとに、最新の書き込みロック・リリースより前のすべてのアクションが発生します。 このモードは、ライターがいつでも破ることのできる、きわめて弱い読取りロックと見なすことができます。 短い読取り専用コード・セグメントにはオプティミスティック読取りモードを使用することで、競合が減少し、スループットが向上することがよくあります。 ただし、その使用は本質的に脆弱です。 オプティミスティック読取りセクションでは、フィールドを読み取り、それらを後で検証してから使用するためにローカル変数に保持することだけを行うべきです。 オプティミスティック読取りモードでは読取りフィールドに一貫性がない場合があるため、データ表現を十分に理解していて一貫性をチェックしたり、メソッドvalidate()を繰り返し呼び出す場合にのみ使用方法が適用されます。 たとえば、最初にオブジェクト参照または配列参照を読み取り、次にそのフィールド、要素、またはメソッドの1つにアクセスする場合、通常そのようなステップが必要になります。

このクラスは、条件に応じて3つのモード間の変換を提供するメソッドもサポートしています。 たとえば、メソッドtryConvertToWriteLock(long)は、モード"upgrade"への試みを行い、(1)がすでに書込みモード(2)の読取りモードである場合に有効な書込みスタンプを返します。さらに、オプティミスティック読取りモードでは、他のリーダーまたは(3)は存在せず、ロックは使用できます。 これらのメソッドの形式は、再試行ベースの設計で発生するコードの膨張をいくから軽減できるように設計されています。

StampedLockは、スレッドセーフなコンポーネントの開発で内部ユーティリティとして使用するために設計されています。 これらを使用するには、保護するデータ、オブジェクト、およびメソッドの内部プロパティの知識が必要です。 これらは再入可能ではないため、ロックされている本体では、ロックの再取得を試みる可能性のある他の不明なメソッドを呼び出すべきではありません(ただし、スタンプを使用または変換できる他のメソッドにスタンプを渡すことはできます)。 読取りロック・モードを使用するには、関連するコード・セクションが副作用を持っていないことが必要です。 未検証のオプティミスティック読取りセクションでは、潜在的な不整合を許容できることがわかっていないメソッドを呼び出すことはできません。 スタンプは有限表示を使用し、暗号的にセキュアではありません(つまり、有効なスタンプが推測可能な場合があります)。 スタンプの値は、1年以上の連続動作の後で再循環する場合があります。 これより長い期間、使用も検証もされずに保持されたスタンプは、正しく検証できない場合があります。 StampedLockは直列化可能ですが、常に初期のロック解除された状態に直列化復元されるため、リモート・ロックには役立ちません。

Semaphoreと同様ですが、ほとんどのLock実装とは異なり、StampedLocksは所有権の概念を持ちません。 あるスレッドで取得されたロックは、別のスレッドで解放または変換できます。

StampedLockのスケジューリング・ポリシーでは、リーダーとライターのどちらを優先するかに一貫性がありません。 「try」メソッドはすべてベスト・エフォート型であり、スケジューリング・ポリシーや公平性ポリシーに必ずしも準拠していません。 ロックを取得または変換する「try」メソッドからゼロが返された場合、ロックの状態に関する情報は提供されません。それ以降の呼び出しが成功する場合もあります。

このクラスでは、複数のロック・モードを調整する使用方法がサポートされているため、LockインタフェースやReadWriteLockインタフェースは直接実装されません。 ただし、関連する機能セットだけを必要とするアプリケーションでは、StampedLockがasReadLock()asWriteLock()、またはasReadWriteLock()と見なされることもあります。

メモリーの同期化 正常にロックされた場合のいずれかのモードへの影響を及ぼすメソッドのメモリー同期は、「Java言語仕様」の第17章 で説明しているように、「ロック」アクションと同じ効果を持ちます。 書込みモードでメソッドのロックを正常に解除すると、メモリー同期の結果は「ロック解除」アクションと同じになります。 オプティミスティック読取りの使用方法では、最新の書込みモードのロック解除アクションより前のアクションは、将来の検証でtrueが戻された場合にのみ、tryOptimisticReadの後続のアクションが発生することが保証されています。そうでない場合、tryOptimisticRead間の読取りと一貫性のあるスナップショットの取得は保証されません。

使用例。 単純な2次元の点を保持するクラスにおけるいくつかの使用方法を次に示します。 サンプル・コードにはいくつかのtry/catch技法が示されていますが、その本体で例外が発生する可能性はないため、ここではこれらが厳密に必要なわけではありません。

 
 class Point {
   private double x, y;
   private final StampedLock sl = new StampedLock();

   // an exclusively locked method
   void move(double deltaX, double deltaY) {
     long stamp = sl.writeLock();
     try {
       x += deltaX;
       y += deltaY;
     } finally {
       sl.unlockWrite(stamp);
     }
   }

   // a read-only method
   // upgrade from optimistic read to read lock
   double distanceFromOrigin() {
     long stamp = sl.tryOptimisticRead();
     try {
       retryHoldingLock: for (;; stamp = sl.readLock()) {
         if (stamp == 0L)
           continue retryHoldingLock;
         // possibly racy reads
         double currentX = x;
         double currentY = y;
         if (!sl.validate(stamp))
           continue retryHoldingLock;
         return Math.hypot(currentX, currentY);
       }
     } finally {
       if (StampedLock.isReadLockStamp(stamp))
         sl.unlockRead(stamp);
     }
   }

   // upgrade from optimistic read to write lock
   void moveIfAtOrigin(double newX, double newY) {
     long stamp = sl.tryOptimisticRead();
     try {
       retryHoldingLock: for (;; stamp = sl.writeLock()) {
         if (stamp == 0L)
           continue retryHoldingLock;
         // possibly racy reads
         double currentX = x;
         double currentY = y;
         if (!sl.validate(stamp))
           continue retryHoldingLock;
         if (currentX != 0.0 || currentY != 0.0)
           break;
         stamp = sl.tryConvertToWriteLock(stamp);
         if (stamp == 0L)
           continue retryHoldingLock;
         // exclusive access
         x = newX;
         y = newY;
         return;
       }
     } finally {
       if (StampedLock.isWriteLockStamp(stamp))
         sl.unlockWrite(stamp);
     }
   }

   // upgrade read lock to write lock
   void moveIfAtOrigin2(double newX, double newY) {
     long stamp = sl.readLock();
     try {
       while (x == 0.0 && y == 0.0) {
         long ws = sl.tryConvertToWriteLock(stamp);
         if (ws != 0L) {
           stamp = ws;
           x = newX;
           y = newY;
           break;
         }
         else {
           sl.unlockRead(stamp);
           stamp = sl.writeLock();
         }
       }
     } finally {
       sl.unlock(stamp);
     }
   }
 }

Java言語仕様を参照してください:
17.4 メモリー・モデル
導入されたバージョン:
1.8
関連項目: