Java同時:AtomicIntegerソース分析-CASベースの楽観的なロックの実現


悲観ロックと楽観ロック
cpuは時分割多重化されていることはよく知られています.すなわち、cpuのタイムスライスを、異なるthread/processに割り当てて順番に実行し、タイムスライスとタイムスライスの間で、cpu切替を行う必要があります.つまり、プロセスの切替が発生します.切り替えは、クリアレジスタに関連し、データをキャッシュします.次に、新しいthreadに必要なデータを再ロードします.スレッドが一時停止されると、ブロックキューに追加され、一定の時間または条件でnotify()を介してnotifyAll()が呼び覚まされます.リソースが使用できない場合は、cpuを譲り、現在の待機スレッドをブロック状態に切り替えます.リソースまで(共有データなど)が使用可能になったら、スレッドを起動してrunnable状態に入ってcpuスケジューリングを待つようにします.これが典型的な悲観ロックの実装です.独占ロックは悲観ロックであり、synchronizedは最悪の場合を仮定し、他のスレッドが干渉しないことを確保した場合にのみ実行され、他のすべてのロックが必要になる独占ロックです.スレッドが停止し、ロックを保持しているスレッドがロックを解除するのを待つ.
しかし、プロセスの保留およびリカバリ実行中に大きなオーバーヘッドが発生するためです.スレッドがロックを待っている間は、何もできないので、悲観的なロックには大きな欠点があります.たとえば、1つのスレッドにリソースが必要な場合、このリソースの占有時間が短い場合、スレッドが初めてこのリソースを占有すると、このリソースが占有される可能性があります.このスレッドを一時停止すると、すぐにリソースが使用可能であることがわかり、その後、長い時間をかけて再占有ロックが必要になり、時間の代価が非常に高くなります.
だから楽観的なロックの概念があって、彼の核心の構想は、毎回ロックをかけないで衝突がないと仮定してある操作を完成して、もし衝突の失敗のため再び試みて、成功するまで.上記の例では、あるスレッドはcpuを譲らずにwhileをループし、失敗したら成功するまで再試行することができます.したがって、データ競合が深刻でない場合、楽観的なロックはより効果的です.例えばCASは楽観的なロック思想の応用である.
JAvaにおけるCASの実現
AtomicIntegerは原子操作をサポートするIntegerクラスで、AtomicIntegerタイプ変数の増加と減少操作が原子的であることを保証し、複数のスレッドでのデータの不一致の問題が発生しない.AtomicIntegerを使用しない場合、同じIDが同時取得されないように、順次取得されるIDを実現するには、取得のたびにロック操作を行う必要があります.
次に,AtomicIntegerが具体的にどのように原子操作を実現するかをソースコードで見る.
まずincrementAndGet()メソッドを見て、次は具体的なコードです.
public final int incrementAndGet() {
     
        for (;;) {
     
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }


ソースコードを通じて、この方法のやり方はまず現在のvalue属性値を取得し、valueに1を加え、局所的なnext変数に値を付与することであることがわかるが、この2つのステップは非スレッドで安全であるが、内部にはデッドサイクルがあり、成功するまでcompareAndSet操作を続け、つまり修正の根本はcompareAndSet方法の中にある.compareAndSet()メソッドのコードは次のとおりです.
public final boolean compareAndSet(int expect, int update) {
     
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

compareAndSet()メソッドで呼び出されたcompareAndSwapInt()メソッドの宣言は、次のようになります.nativeメソッドです.
publicfinal native boolean compareAndSwapInt(Object var1, long var2, int var4, intvar5);

compareAndSetはメソッドの実行時に取得したvalue属性値、nextはプラス1の値を入力し、compareAndSetはSunのUnSafeを呼び出すcompareAndSwapIntメソッドとして完成し、このメソッドはnativeメソッドであり、compareAndSwapIntはCPUのCAS命令に基づいて実現される.したがって、CASベースの動作はブロックされていないと考えられ、1つのスレッドの失敗または停止は、他のスレッドも失敗または停止を引き起こすことはありません.またCAS操作はCPU原語なので性能が良い.
同様に、decrementAndGet()メソッドもあります.incrementAndGet()との違いは、valueを1ずつ減らし、next変数に値を割り当てることです.
AtomicIntegerにはgetAndIncrement()とgetAndDecrement()の方法もあります.彼らの実現原理は上記の2つの方法と完全に同じで、違いは戻り値が異なり、前の2つの方法は変更後の値、すなわちnextを返します.この2つの方法は、変更前の値、すなわちcurrentを返します.他にもたくさんの方法があるので、列挙しません.
テキストリンク:https://www.fangzhipeng.com/javainterview/2019/03/16/atomicinteger-cas.html