0Pricing
Java Academy · レッスン

StampedLock と楽観的読み取り

読み取りが多く高スループットが求められる処理に、StampedLock の楽観的読み取りモードを適用します。

「StampedLock と楽観的読み取り」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

StampedLockとは

Java 8で導入されたStampedLockは、ReadWriteLockの概念を拡張し、追加の楽観的読み取りモードを提供します。操作は、ロックの解放や楽観的読み取りの検証に使用するlong型のスタンプを返します。

import java.util.concurrent.locks.*;

StampedLock sl = new StampedLock();

// Exclusive write:
long stamp = sl.writeLock();
try {
    // write data
} finally {
    sl.unlockWrite(stamp);
}

// Shared read:
stamp = sl.readLock();
try {
    // read data
} finally {
    sl.unlockRead(stamp);
}

楽観的読み取りモード

楽観的読み取りではロックを取得せず、スタンプだけを取得します。読み取り後にスタンプを検証してください。検証に失敗した場合(書き込みが割り込んだ場合)は、通常の読み取りロックに切り替えます:

StampedLock sl2 = new StampedLock();
double x = 0, y = 0;

// Try optimistic read first:
long stamp = sl2.tryOptimisticRead();
double localX = x, localY = y; // read values

if (!sl2.validate(stamp)) {
    // A write occurred — fall back to read lock
    stamp = sl2.readLock();
    try {
        localX = x;
        localY = y;
    } finally {
        sl2.unlockRead(stamp);
    }
}
// use localX and localY

Point2Dの完全な例

楽観的読み取りを使用した、StampedLockの典型的な例です。2D座標上の点を扱います:

class Point {
    private double x, y;
    private final StampedLock lock = new StampedLock();
    
    void move(double dx, double dy) {
        long stamp = lock.writeLock();
        try { x += dx; y += dy; }
        finally { lock.unlockWrite(stamp); }
    }
    
    double distanceFromOrigin() {
        long stamp = lock.tryOptimisticRead();
        double cx = x, cy = y;
        if (!lock.validate(stamp)) {
            stamp = lock.readLock();
            try { cx = x; cy = y; }
            finally { lock.unlockRead(stamp); }
        }
        return Math.sqrt(cx * cx + cy * cy);
    }
}

ロックの変換

StampedLockはロックモード間の変換に対応しています:

StampedLock sl3 = new StampedLock();

// Read → Write upgrade attempt:
long readStamp = sl3.readLock();
long writeStamp = sl3.tryConvertToWriteLock(readStamp);
if (writeStamp != 0) {
    // Successfully upgraded!
    // ... write ...
    sl3.unlockWrite(writeStamp);
} else {
    // Upgrade failed — release read and reacquire write
    sl3.unlockRead(readStamp);
    writeStamp = sl3.writeLock();
    // ... write ...
    sl3.unlockWrite(writeStamp);
}

楽観的読み取りが最も役立つ場合

楽観的読み取りは、次のような場合に効果を発揮します:

  • 書き込みより読み取りが大幅に多い場合
  • データのスナップショットが少数のローカル変数に収まる場合(オブジェクトを割り当てない場合)
  • 書き込みの競合が少ない場合(検証が失敗することがまれな場合)

書き込みが頻繁な場合、楽観的読み取りは何度も完全な読み取りロックに切り替わるため、メリットがありません。

StampedLockとReadWriteLockの比較

主な違い:

  • StampedLock: 楽観的読み取りを追加。再入可能ではなく、Conditionにも対応していません
  • ReentrantReadWriteLock: 再入可能で、Conditionに対応。楽観的モードはありません

StampedLockは書き込みの競合が少ない場合に高速です。ReentrantReadWriteLockはより安全で、機能も豊富です。

StampedLockは再入可能ではない

ReentrantLockとは異なり、StampedLockは再入可能ではありません。すでに保持しているモードを同じスレッドが取得しようとすると、デッドロックが発生します:

StampedLock sl4 = new StampedLock();
long s1 = sl4.readLock();
// DO NOT: sl4.readLock() again — deadlocks with non-reentrant!

tryWriteLockとtryReadLock

ブロックせずに取得を試み、失敗した場合は0を返します:

StampedLock sl5 = new StampedLock();

long stamp = sl5.tryWriteLock();
if (stamp != 0) {
    try {
        System.out.println("Got write lock");
    } finally {
        sl5.unlockWrite(stamp);
    }
} else {
    System.out.println("Write lock unavailable");
}

ReadWriteLockとして使用する

StampedLockは、互換性のために標準のReadWriteLockとしてビューを返すことができます:

StampedLock sl6 = new StampedLock();
Lock readLock  = sl6.asReadLock();
Lock writeLock = sl6.asWriteLock();
ReadWriteLock rwView = sl6.asReadWriteLock();
// These provide standard Lock interface without optimistic mode

パフォーマンスベンチマークの傾向

ベンチマーク結果には、通常次のような傾向が見られます:

  • 読み取りが非常に多い(読み取り99%): StampedLock > ReadWriteLock > synchronized
  • 混合ワークロード(読み取り70%): ReadWriteLock ≈ StampedLock
  • 書き込みが多い: オーバーヘッドが少ないsynchronizedまたはReentrantLockが勝つことがよくあります

最適化する前に、必ず現実的なワークロードでプロファイリングしてください。

アンロック後のスタンプの無効化

スタンプを使ってロックを解放すると、そのスタンプは無効になります。アンロック後に古いスタンプを使用すると未定義動作につながるため、ロックを取得するたびに必ず新しいスタンプを取得してください。

StampedLock sl7 = new StampedLock();
long stamp = sl7.writeLock();
sl7.unlockWrite(stamp);
// stamp is now invalid — don't use it:
// sl7.unlockWrite(stamp); // throws IllegalMonitorStateException or corrupts state

クイックチェック

tryOptimisticRead()による楽観的読み取りの後にvalidate(stamp)を呼び出すとfalseが返りました。次にコードは何をすべきですか?

まとめ: StampedLock

重要なポイント:

  • 3つのモードがあります。楽観的読み取り(ロックなし)、読み取りロック、書き込みロックです
  • tryOptimisticRead() + validate() — コストゼロの読み取り試行
  • 検証に失敗した場合 → readLock()に切り替えます
  • 再入可能ではありません。同じスレッドが再取得するとデッドロックの危険があります
  • Conditionには対応していません。必要な場合はReentrantReadWriteLockを使用します

よくある質問

「StampedLock と楽観的読み取り」レッスンは無料ですか?

はい。「StampedLock と楽観的読み取り」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「StampedLock と楽観的読み取り」で何を学びますか?

読み取りが多く高スループットが求められる処理に、StampedLock の楽観的読み取りモードを適用します。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「StampedLock と楽観的読み取り」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ReentrantLock と synchronized の比較
  2. 読み取り・書き込み処理のための ReadWriteLock
  3. Atomic 変数:ロックなしの更新
  4. StampedLock と楽観的読み取り
← Java Academyに戻る