【Java学習|初心者向け】マルチスレッド開発の必須知識!Atomic変数を使いこなしてスレッドセーフなコードを書こう

1. 導入: なぜAtomicクラスが重要なのか?

Javaでマルチスレッドプログラミングを行う際、複数のスレッドから同時に同じ変数にアクセスすると、データの不整合(競合状態)が発生することがあります。通常はsynchronizedキーワードを使って排他制御を行いますが、パフォーマンスが低下する懸念があります。そこで登場するのがjava.util.concurrent.atomicパッケージです。これを使うと、ロックを使わずに「アトミック(不可分)」な操作が可能になり、安全かつ高速にデータを更新できるようになります。

2. 基礎知識: 「アトミック」とは何か?

プログラミングにおける「アトミック」とは、操作が途中で分断されることなく、一瞬で完了することを意味します。
例えば、int型の変数に「1を足す」という操作は、内部的には「読み込み→加算→書き込み」の3ステップで行われます。マルチスレッド環境では、この途中で別のスレッドが割り込む可能性があるため、値が正しく更新されないことがあります。Atomicクラスはこの3ステップをCPUレベルで「一つの操作」として完結させるため、スレッドセーフを保証できるのです。

3. 実装/解決策: 代表的なAtomicクラスの使い分け

Atomicクラスには扱うデータ型に応じていくつか種類があります。
AtomicInteger / AtomicLong: 整数値の加算や減算を安全に行います。カウンタなどに最適です。
AtomicBoolean: フラグ管理に使用します。
AtomicReference: オブジェクトの参照をアトミックに入れ替える際に使用します。

4. サンプルプログラム: カウンタの実装例

以下のコードは、複数のスレッドから同時にカウンタを増やす例です。通常のint型では失敗することが多い処理ですが、AtomicIntegerなら安全です。

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class AtomicSample {
    // AtomicIntegerを初期値0で生成
    private static AtomicInteger counter = new AtomicInteger(0);

    public static void main(String[] args) throws InterruptedException {
        // 仮想スレッドを利用して1000回カウントアップを行う
        ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

        for (int i = 0; i < 1000; i++) {
            executor.submit(() -> {
                // インクリメント操作をアトミックに行う
                counter.incrementAndGet();
            });
        }

        executor.shutdown();
        executor.awaitTermination(5, TimeUnit.SECONDS);

        // 結果を表示(1000になれば成功)
        System.out.println("最終的なカウント値: " + counter.get());
    }
}

5. 応用・注意点: 現場で役立つポイント

Atomicクラスは便利ですが、万能ではありません。注意すべきポイントは以下の通りです。

注意点1: 複数の変数をまたぐ操作はできない
Atomicクラスが保証するのは「その変数単体」の操作のみです。「AとBの両方を同時に更新したい」という場合は、Atomicクラスを個別に使っても整合性は保てません。その場合はsynchronizedやLockインターフェースを使用してください。

注意点2: 複雑なロジックはcompareAndSetを活用する
単純な加算だけでなく、「今の値がAならBにする」といった条件付き更新を行いたい場合は、compareAndSet(期待値, 更新値)メソッドを使用します。これは現代のJava並行処理の基礎となる「CAS(Compare-And-Swap)」アルゴリズムに基づいています。

まとめ
JavaのAtomicクラスは、ロックのオーバーヘッドを抑えたい場合に非常に強力な武器になります。特に最近のJavaで導入されたVirtual Threadsと組み合わせることで、大量のタスクを効率的に処理するアプリケーション開発が可能になります。まずはカウンタやフラグ管理から置き換えてみてください。

コメント

タイトルとURLをコピーしました