JavaにおけるAtomicInteger

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

アトミックインテジャー

シンプルなマルチスレッドプログラムを作成しましょう。すべてのスレッドが共有カウント変数を4回増やします。つまり、2つのスレッドがある場合、処理が完了した後のカウント値は8になります。JavaAtomic.java

package com.scdev.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) {
        // processing some job
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

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

JavaのAtomicIntegerの例

常にカウント値が8となるアップデートされたプログラムがこちらです。なぜなら、AtomicIntegerメソッドのincrementAndGet()は現在の値を1でアトミックにインクリメントするからです。

package com.scdev.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) {
        // processing some job
        try {
            Thread.sleep(i * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

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

コメントを残す 0

Your email address will not be published. Required fields are marked *