Java AtomicInteger原子タイプの詳細な解析

7083 ワード

同時プログラミングを行う際には、プログラムが複数のスレッドに同時アクセスされたときに正しい結果を得ることができること、すなわちスレッドセキュリティを実現することを確保する必要があります.スレッドセキュリティの定義は次のとおりです.
           ,                            ,                     ,             ,            。


スレッドが安全ではない例を挙げます.Webアクセス量を統計する機能を実現したい場合は、count++でアクセス量を統計したいかもしれませんが、この自己増加操作はスレッドが安全ではありません.count++は3つの操作に分けることができます.
変数の現在値を取得取得取得取得して取得した現在の変数値+1に新しい値を入力し、変数にcountの初期値が10であると仮定し、同時操作を行うと、スレッドAとスレッドBの両方が1操作になり、その後2操作が同時に行われる可能性がある.Aはまず3操作+1まで行い、現在値は11である.注意先ABで取得した現在値はすべて10であるため,Bが3操作を行った後もcountの値は11である.この結果は明らかに私たちの要求に合わない.
そこで、本編の主役であるAtomicIntegerを使ってスレッドの安全を保証する必要があります.
AtomicIntegerのソースコードは次のとおりです.

package java.util.concurrent.atomic;
import sun.misc.Unsafe;

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    public AtomicInteger() {
    }

    public final int get() {
        return value;
    }

    public final void set(int newValue) {
        value = newValue;
    }

    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

    public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final boolean weakCompareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;
        }
    }
  
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;
        }
    }

    public String toString() {
        return Integer.toString(get());
    }


    public int intValue() {
    return get();
    }

    public long longValue() {
    return (long)get();
    }

    public float floatValue() {
    return (float)get();
    }

    public double doubleValue() {
    return (double)get();
    }

}


まず、原子型クラスで定義されている属性を見てみましょう.
  // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;

    static {
      try {
        valueOffset = unsafe.objectFieldOffset
            (AtomicInteger.class.getDeclaredField("value"));
      } catch (Exception ex) { throw new Error(ex); }
    }

UnsafeはJDK内部のツールクラスであり,主にプラットフォーム関連の操作を実現している.以下の内容は
[sun.misc.Unsafe](http://hg.openjdk.java.net/jdk7u/jdk7u/jdk/file/tip/src/share/classes/sun/misc/Unsafe.java) JDK       。       Java    “   ”    Java   ,  JDK       Java               、    native  (  C C++)        。      JDK        。

Unsafeの具体的な実装は本編の目標とは大きく関連していません.このコードがスタックメモリ内のvalueのオフセット量を取得するためであることを知っていれば十分です.オフセット量はAtomicIntegerにおいて重要であり,原子操作はすべてそれによって実現される.
Valueの定義とvolatile AtomicInteger自体は整数型なので、最も重要な属性はvalueです.valueを宣言する方法を見てみましょう.
private volatile int value;

valueがvolatile修飾子を使用しているのを見ましたが、volatileとは何ですか?
volatileはsynchronizedの弱い実装に相当し、すなわちvolatileはsynchronizedのような意味を実現したが、ロックメカニズムはない.volatileフィールドの更新が他のスレッドに予見可能に通知されることを保証します.
volatileには、次の意味が含まれます.
Java        valatile          :     volatile                  。
volatile            (        )     CPU      ,          volatile     。      volatile     ,         ,                。    happens-before   ,   valatile       ,                   。


簡単に言えばvolatileの役割は、あるスレッドが共有変数を変更した場合、別のスレッドがこの変更後の値を読み取ることができることです.AtomicIntegerのソースコードを解析する際には、ここで十分であることがわかります.
簡単に言えばvolatileの役割は、あるスレッドが共有変数を変更した場合、別のスレッドがこの変更後の値を読み取ることができることです.AtomicIntegerソースコードを解析する際には,ここで十分であることが分かった.
CAS操作で安全な自己増加を実現
AtomicIntegerには、incrementAndGet()がi++に相当し、getAndAdd()がi+=nに相当するなど、多くの方法があります.ソースコードからこのいくつかの方法の実装が似ていることが分かるので,incrementAndGet()法のソースコードを主に解析した.
ソースコードは次のとおりです.
 public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

 public final boolean compareAndSet(int expect, int update) {
    return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

incrementAndGet()法は自己増加動作を実現した.コアインプリメンテーションは、現在の値とターゲット値(value+1)を先に取得し、compareAndSet(current,next)が正常に戻ると、メソッドはターゲット値を返します.ではcompareAndSetは何をしていますか?この方法を理解するにはCAS操作を導入する必要がある.
大学のオペレーティングシステムの授業で、独占ロックと楽観ロックの概念を学んだことがあります.独占ロックとは、スレッドがロックを取得した後、他のスレッドが独占ロックを持つスレッドがロックを解除するまで停止する必要があることである.楽観的ロックは、競合がないと仮定して直接操作し、競合があるために失敗した場合は操作が成功するまで再試行します.その中で楽観ロックが用いられるメカニズムはCAS,Compare and Swapである.
AtomicIntegerのCAS操作はcompareAndSet()であり、メモリからメモリオフセット量(valueOffset)に基づいてデータを取り出すたびに、取り出した値をexpectと比較し、データが一致すればメモリの値をupdateに変更する役割を果たす.
このようにCASを用いることで原子操作が保証される.残りのいくつかの方法の原理はこれと同じで、ここではあまり説明しません.
AtomicIntegerソースコードを見る前に、その内部はsynchronizedで実現された原子操作だと思います.資料を調べるとsynchronizedは性能に影響することが分かった.Javaのsynchronizedロックは独占ロックであり、原子操作を実現できるが、この実現方式の同時性能は悪いからだ.
まとめてみると,AtomicIntegerでは主に整数型の原子操作を実現し,同時発生時の異常結果を防止し,その内部は主にJDKにおけるunsafe系操作メモリのデータに依存して実現されている.volatile修飾子は、valueがメモリ内の他のスレッドで変更に値することを保証します.CAS操作は、AtomicIntegerがvalueの値を安全に変更できることを保証します.