P-01: スレッドセーフな採番(AtomicLong / LongAdder)
複数スレッドから同時に番号を採番すると、通常の long++ では重複が発生します。AtomicLong は CAS(Compare-And-Swap)命令で synchronized なしに ATOMIC 操作を実現します。 注文番号・リクエストIDなど、業務システムで必須のパターンです。
AtomicLong vs LongAdder の使い分け
| クラス | 特徴 | 向いている用途 |
|---|---|---|
| AtomicLong | CAS 命令で厳密な連番を保証 | 注文番号・リクエストID・シーケンス番号 |
| LongAdder | 内部セル分散で高並行時に高速 | アクセスカウンター・統計集計 |
synchronized | ロックで排他制御、低オーバーヘッド時有効 | 複合操作(複数フィールドの同時更新) |
サンプルコード
よくあるミス・注意点
⚠️ long++ はスレッドアンセーフ
counter++ は「読み取り・加算・書き込み」の3命令です。 スレッド A が読み取り中にスレッド B が同じ値を読むと、同じ番号が2回採番されます。 必ず AtomicLong.incrementAndGet() を使ってください。
⚠️ LongAdder は一意の連番には使えない
LongAdder の sum() は内部セルの合計値を返しますが、 取得タイミングによって「最終的な正確な合計値」と「瞬間的な近似値」が異なります。 注文番号のように一意性・連番性が必要な用途には AtomicLong を使ってください。
⚠️ AtomicLong は単一 JVM 内でのみ有効
複数サーバ(マルチ JVM)が同じ採番を共有する場合、AtomicLong では サーバをまたいだ一意性は保証できません。その場合は P-02(DB 採番)を使ってください。
テストする観点
- ✅ 100スレッド × 1000回インクリメントした結果が 100,000 と一致するか
- ✅
incrementAndGet()の戻り値に重複がないか(採番値セットの確認) - ✅ シングルトンの
OrderNumberGenerator.next()が並列実行時も一意か - ✅
LongAdderのsum()が全加算後に期待値と一致するか - ✅ 初期値(
new AtomicLong(10000))から始まる番号列が正しいか