JavaにおけるAtomicInteger

今日はJavaでAtomicIntegerを見ていきます。アトミック操作は、他の操作からの干渉なしに単一のタスク単位で実行されます。マルチスレッド環境ではデータの不整合を避けるために、アトミック操作が必要です。

AtomicInteger

共有のcount変数を4回増やす単純なマルチスレッドプログラムを作成しましょう。したがって、2つのスレッドがある場合、countの値は8になるはずです。JavaAtomic.java

package com.journaldev.concurrency;

public class JavaAtomic {

    public static void main(String[] args) throws InterruptedException {

        ProcessingThread pt = new ProcessingThread();
        Thread t1 = new Thread(pt, "t1");
        t1.start();
        Thread t2 = new Thread(pt, "t2");
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Processing count=" + pt.getCount());
    }

}

class ProcessingThread implements Runnable {
    private int count;

    @Override
    public void run() {
        for (int i = 1; i < 5; i++) {
            processSomething(i);
            count++;
        }
    }

    public int getCount() {
        return this.count;
    }

    private void processSomething(int i) {
        // いくつかのジョブを処理する
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

上記のプログラムを実行すると、countの値が5、6、7、8の間で変動することに気付くでしょう。その理由は、count++がアトミック操作ではないためです。したがって、1つのスレッドが値を読み取り、1を増やすときに、他のスレッドが古い値を読み取って誤った結果につながります。この問題を解決するには、countの増分操作がアトミックであることを確認する必要があります。これは同期を使用して行うことができますが、Java 5のjava.util.concurrent.atomicは同期を使用せずにこのアトミック操作を実現するためのintとlongのラッパークラスを提供しています。

Java AtomicIntegerの例

現在の値を1つ増やす`AtomicInteger`メソッド`incrementAndGet()`が、常にカウント値を8として出力するようになったプログラムを以下に示します。

package com.journaldev.concurrency;

import java.util.concurrent.atomic.AtomicInteger;

public class JavaAtomic {

    public static void main(String[] args) throws InterruptedException {

        ProcessingThread pt = new ProcessingThread();
        Thread t1 = new Thread(pt, "t1");
        t1.start();
        Thread t2 = new Thread(pt, "t2");
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Processing count=" + pt.getCount());
    }
}

class ProcessingThread implements Runnable {
    private AtomicInteger count = new AtomicInteger();

    @Override
    public void run() {
        for (int i = 1; i < 5; i++) {
            processSomething(i);
            count.incrementAndGet();
        }
    }

    public int getCount() {
        return this.count.get();
    }

    private void processSomething(int i) {
        // いくつかのジョブを処理
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

アトミック操作のために並行クラスを使用する利点は、同期の心配が不要であることです。これによりコードの読みやすさが向上し、エラーの可能性が減少します。また、アトミック操作の並行クラスは、リソースのロックを含む同期よりも効率的であると想定されています。

Source:
https://www.digitalocean.com/community/tutorials/atomicinteger-java