Javaの、CASのロックフリーアルゴリズム

 スレッドが失敗したり、他のスレッドを中断していない場合も、障害が発生するか、そしてこのアルゴリズムが呼び出されたハングノンブロッキング・アルゴリズムをCASは、非ブロッキングアルゴリズムの一種でも楽観的ロックの技術です。それは、ロックを使用せず、またそうCASロックフリーアルゴリズムを複数のスレッド間で変数の同期を達成することができます。

1、CASの実装

 メモリ位置Vを読み書きする必要がある、新しい値A及びBの比較提案書き込み - CASは、3つのオペランドを含みます 値がV Aに等しい場合にのみ、かつ、CASがV原子の値によって新しい値Bで更新されます、または新しい値を書き込まない場合は、のは、CASのアナログ実装を見てみましょう。

    //CAS模拟实现
    public class SimulatedCAS {
        private int value;
    
        public synchronized int get() {
            return value;
        }
    
        public synchronized int compareAndSwap(int expectedValue, int newValue) {
            int oldValue = value;
            if (oldValue == expectedValue) {
                value = newValue;
            }
            return oldValue;
        }
    
        public synchronized boolean compareAndSet(int expectedValue, int newValue) {
            return (expectedValue == compareAndSwap(expectedValue, newValue));
        }
    }
复制代码

SimulatedCASシミュレーションのみCASがあるので、CASはその意味を理解し、アナログ実装なので、コードは非常に簡単です。

    //基于CAS实现的一个线程安全计数器
    public class CasCounter {
        private SimulatedCAS value;

        public int getValue() {
            return value.get();
        }

        public int increment() {
            int v;
            do {
                v = value.get();
            } while (v != value.compareAndSwap(v, v + 1));
            return v + 1;
        }
    }
复制代码

CasCounter基づいてSimulatedCAS実装され、スレッドセーフなカウンター、CasCounterそれはブロックされませんが、他のスレッドが同時に更新カウンタを更新する場合、それは複数回再試行します。理論的には、他のスレッドが再試行するたびに、スレッド、あなたがCASを競うたびに勝つために常にある場合は、しかし、飢餓の問題のこのタイプの実際には非常に少ないです。

2、アトミック変数クラス

 Java5.0基礎となるint型で導入サポート、長い、およびオブジェクト参照も、CAS操作の他のタイプ、およびJVMは、基盤となるハードウェアを提供し、それらをコンパイルするための最も効果的な方法を開示しています。プラットフォームでは、ランタイムは、適切な(複数の)マシン命令としてそれらをコンパイルし、CASをサポートしています。最悪の場合、CASは命令をサポートしていない場合、JVMは、スピンロックを使用します。そのため、密接に関連プラットフォーム/コンパイラでJavaが提供するCASソリューションは、その性能にも影響を与えるプラットフォーム/コンパイラの影響を受けています。

 アトミック変数クラス(例えばjava.util.concurrent.atomicにおけるAtomicXxxこの高効率CAS操作の使用に関する)。

タイプ 実装クラス 説明
基本データ型 AtomicInteger 整数型アトミック更新
AtomicBoolean ブール型のアトミック更新
AtomicLong ロングタイプの原子更新
配列型 AtomicIntegerArray アトミック更新配列型の整数
AtomicLongArray 長い配列型アトミック更新
AtomicReferenceArray アトミック更新対象の配列型
参照型 AtomicReference アトミック更新オブジェクト
AtomicStampedReference アトミック更新オブジェクトは、ABAの問題を解決します
AtomicMarkableReference アトミック更新オブジェクトは、ABAの問題を解決します
更新フィールドタイプ AtomicIntegerFieldUpdater 既存のタイプの揮発性アトミック更新整数、リフレクションを使用して実装しました
AtomicLongFieldUpdater 既存のロングタイプの揮発性アトミック更新は、リフレクションを使用して実装しました
AtomicReferenceFieldUpdater 達成するためにリフレクションを使用してオブジェクトを既存の揮発性アトミック更新

 上記のすべてJavaでアトミック変数クラスで、基本的には良く理解されているが、個々のはまだいくつかの明確化が必要です。

AtomicStampedReferenceそしてAtomicMarkableReference彼らは、解決するために設計されてABAの問題が存在します。しかし、彼らが異なっている、AtomicStampedReference避けるようにオブジェクトプラスバージョン番号への参照であるABAの問題を、そしてAtomicMarkableReference回避するために、オブジェクトプラスマーク(boolean型)への参照であるABAの問題を。

 ドメインアップデータ原子と呼ばれるアップデートのフィールドタイプ、従来のvolatile「ビュー」の反射に基づく1は、従来では可能であるvolatileCASフィールドの使用。これは、クラスの平均原子番号よりも弱い保証の原子性を提供し、それは直接基本フィールドを直接変更していないことを確認することはできません- compareAndSet他の方法は、他の方法アップデータスレッドを使用して、アトミック演算を確保することができます。

 以下の点を考慮AtomicIntegerのソースコードを。

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可以说是value在JVM中的虚拟内存地址
            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;
    }

    //1.6新增方法,设置新值,但不会立即刷新,性能比set方法好
    public final void lazySet(int newValue) {
        unsafe.putOrderedInt(this, valueOffset, newValue);
    }

    //设定新值并返回旧值
    public final int getAndSet(int newValue) {
        return unsafe.getAndSetInt(this, valueOffset, newValue);
    }

    //比较并替换,Java CAS实现方法
    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    //比较并替换,Java CAS实现方案,与compareAndSet实现一样名单不排除未来会有不同实现
    public final boolean weakCompareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    //以原子方式将当前值增加1。返回旧值
    public final int getAndIncrement() {
        return unsafe.getAndAddInt(this, valueOffset, 1);
    }

    //以原子方式将当前值减1。返回旧值
    public final int getAndDecrement() {
        return unsafe.getAndAddInt(this, valueOffset, -1);
    }

    //以原子方式将给定值添加到当前值。返回旧值
    public final int getAndAdd(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    //以原子方式将当前值增加1。返回新值
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }

    //以原子方式将当前值减1。返回新值
    public final int decrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;
    }

    //以原子方式将给定值添加到当前值。返回新值
    public final int addAndGet(int delta) {
        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
    }

    //1.8新增方法,更新并返回旧值
    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }

    //1.8新增方法,更新并返回新值
    public final int updateAndGet(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return next;
    }

    //1.8新增方法,更新并返回旧值
    public final int getAndAccumulate(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return prev;
    }

    //1.8新增方法,更新并返回新值
    public final int accumulateAndGet(int x,
                                      IntBinaryOperator accumulatorFunction) {
        int prev, next;
        do {
            prev = get();
            next = accumulatorFunction.applyAsInt(prev, x);
        } while (!compareAndSet(prev, next));
        return next;
    }
    //都是些类型转换的实现
    ...
}
复制代码

valueOffsetCASは、メモリ位置Vに言われているJVMのメモリアドレス、変数であります setそしてlazySetcompareAndSetweakCompareAndSet方法の二組を見てする必要があります。

 のでvalueれるvolatile修正お電話の際は、そのset方法に、すべてのスレッドのvalue値はすぐに更新され、そしてlazySet、唯一の時間は、新しい値が更新され、再書き込み、まだ古い値を読み取ることはありません。これらの2つの方法の違いで、参照が可能JUCはAtomicXXXは区別ツール、セット、およびlazySet原子SET対のAtomicInteger lazySet FCの 2の記事を。

compareAndSetそして、weakCompareAndSetまさに同じことを達成するため、これらの2つの方法は、より興味深いものです、それはなぜナイジェリアのですか?スタックオーバーフローこれらの2つの方法の違いを参照することができますについての言うように、Javaは、将来的に異なる実装を有することができるweakCompareAndSet場合は、完全のcompareAndSetとして、どのように失敗した場合- Javaの?この記事。

 でAtomicInteger、その他のアトミック変数クラスが使用しているUnsafe、それは安全でないため、CAS、それらのほとんどは、ネイティブメソッドおよび一般に公開されていないクラスを実装します。このクラスのハードウェア関連、CPUの命令レベルの操作されている、唯一の一歩アトミック操作するだけでなく、ロックを決定するために、オペレーティングシステムを要求してCASの問題を回避するには、取得するためにCPU内で直接オペレーティングシステムを気にしないので、効率が非常に高く、スレッドの安全性。

三の大問題に関する3、CASアトミック操作

 CASは、次のような問題です。

  • ABAの問題。
  • CASは、CPUの命令レベルの操作ですので、高いCPU負荷を引き起こす可能性があるとき、サイクル時間が長すぎます。
  • 変数の同時動作トラブルのビットを共有する複数の変数を操作することができます。あなたはできるAtomicReference解決されます。

 ABAの問題は、VはAに等しい場合にのみ、CASがアトミックで新しい値B値Vで更新する場合は、意味論を検討するCAS、CASで珍しい現象で、それ以外の場合は何もしないだろう。最初の値がV Cに変更してからAに変更されたのであれば、その後、CAS操作の他のスレッドがまだ成功することができますが、これは正しいですか?Vの値が変更されたため、それは、明らかに間違っています。それでは、どのようナイジェリアを解決するには?変更プロセスは、「A-B-A」は、「1A-2B-3A」となるからであるように、実際には、変数は、バージョン番号、毎回更新された変数についてのバージョン番号プラスワンの前に添加することができます。

4、要約

 Javaでは、CASの使用はでJava 1.8、アトミックオブジェクトが作成など、非常に広範囲でありConcurrentHashMap且つRxJavaマルチスレッド変数同期は、CASによって達成されます。

[参考文献]

Javaの「ロック」なことを言っていないではありません

の原則のJAVA CASの詳細な分析

同期アルゴリズムとCAS(比較とスワップ)ロックフリーアルゴリズムをノンブロッキング

サイケ] [Javaの並行処理 - CASの深さ分析

Javaの並行処理 - ロックなしのCASと安全ではないクラスとの契約アトミック

心身、ロボトミー、ソウ

おすすめ

転載: juejin.im/post/5d11eefff265da1bcc194feb