CAS操作とAtomicInterger

2571 ワード

CAS:Compare and swapは、原子操作の一種です.CAS操作には、V(メモリ位置)、A(予想値)、(B)の3つの値が含まれており、CAS操作を行う場合は、変数が存在するメモリ位置を優先的に知り、この変数を判断し、この値が予想値と等しい場合は、新しい値Bを元の値Aに置き換え、メモリVに入れる.
CAS操作に対して,同時概念の中原子性,可視性,秩序性における原子性を保証した.Javaにとって、JVMのメモリモデルは秩序性を保証します.JAvaのキーワードvolatileは、可視性を保証します.従ってvolatile修飾AtomicXXX変数の動作は基本的にスレッドが安全であると考えられる.AtomicIntegerのソースコードを読むことで、元の変数valueがvolatileで修飾されていることがわかります.そのため、AtomicXX変数の操作はスレッドが安全です.
    private volatile int value;

また,原子操作については,単純に1つの文が1つの原子操作であるとは考えられず,これについてアセンブリを熟知している可能性が多い.例えば、次のいくつかの操作について
1. i++       // 1.        i  2.      +1   3.      i      
2. i = j      // 1.      j ,3.  j       i     
3. i += 10 //   1
4. i = 10 //          ,    ,        i      

Atomic変数を使用すると、ロックよりも効率が高くなります!これは,Atomic変数を操作する際にスレッドがブロックされず,同期コードブロックを使用するとスレッドがブロックされ,プログラムの複雑さが増すためである.
public class AtomicTester {
    //private AtomicInteger value;
    private Integer value;
    private long start;
    public void test() {
        //value = new AtomicInteger(1);
        start = System.currentTimeMillis();
        value = 0;
        for (int count = 0;count < 4 ;count++){
            new Thread(() -> {
                synchronized (value) {
                    while (true) {
                        value++;
                        isGetThere();
                        try {
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
        }
    }

    public void isGetThere() {
        //    if (value.get() == 1000){
        if (value.equals(1000)){
            System.out.println("use time " + (System.currentTimeMillis() - start));
            System.exit(0);
        }
    }
}

以上のコードを用いてテスト(コメントを外して比較)を行ったところ,synコードブロックを用いた場合は35秒程度,原子変数を用いた場合は20秒程度かかることが分かった.スレッド数が増加すると、パフォーマンスの差が大きくなります.
ABA問題:CASは操作値の時に下の値に変化がないかをチェックする必要があるので、変化がなければ更新しますが、一つの値が元のAで、Bになって、またAになった場合、CASを使って検査するとその値は変化していないことがわかりますが、実際には変化しています.ABA問題の解決策はバージョン番号を使うことです.変数の前にバージョン番号を追加し、変数が更新されるたびにバージョン番号を1つ追加すると、A-B-Aは1 A-2 B-3 Aになります.Java 1から5からJDKのatomicパッケージにABA問題を解決するためのクラスAtomicStampedReferenceが提供された.このクラスのcompareAndSetメソッドは、まず、現在の参照が所望の参照に等しいかどうかを確認し、現在のフラグが所望のフラグに等しいかどうかを確認し、すべてが等しい場合、その参照とフラグの値を所定の更新値に原子的に設定することです.
非元の変数については、実行可能な動作は限られているが、AtomicReferenceを使用して操作することができる.基本的にはCASに限られる.しかし,マルチスレッドアクセスの変数の操作が複雑でない場合には,この方式が考えられる.