ホームページ >Java >&#&チュートリアル >CAS と Java オプティミスティック ロックの使用方法
CASとはCompareAndSwap、つまり比較と交換のことです。なぜ CAS はロックを使用しないのに、同時条件下でも安全なデータ操作を保証するのですか? その名前は実際に CAS の原理を非常に直観的に示しています。データを変更する具体的なプロセスは次のとおりです:
Use CAS 操作 データが生成されると、データの元の値と変更される値をメソッドに渡します
現在のターゲット変数の値が渡された元の値と同じであるかどうかを比較しますin
これらが同じ場合は、ターゲット変数が他のスレッドによって変更されていないことを意味します。ターゲット変数の値を直接変更してください。
#ターゲット変数の値が元の値と異なる場合は、ターゲット変数が変更されたことが証明されます。他のスレッドによって変更されており、この CAS 変更は失敗しました
From上記のプロセスでは、CAS が実際にデータの安全な変更を保証していることがわかりますが、変更、つまりターゲット変数データの変更が失敗する可能性があります。変更が失敗した場合、この時点で、ループしてデータの変更を決定する必要があります。 CAS がデータを変更した結果を確認し、失敗した場合は再試行します。
注意深く考える学生は、CAS 自体の比較と置換操作によって同時実行のセキュリティ上の問題が発生するのではないかと心配するかもしれませんが、実際のアプリケーションではそのような状況は起こりません。比較と置換は JDK によって実行されます。ハードウェア レベルの CAS オリジナルのヘルプ。比較と置換がアトミックなアクションであることを保証するためのイディオム。
ロックフリー プログラミングとは、ロックを使用せずに共有変数を安全に操作することを指します並行プログラミングでは、さまざまなロックを使用します共有変数のセキュリティを確保します。つまり、1 つのスレッドが共有変数の操作を完了していない場合、他のスレッドは同じ共有変数を操作できないことが保証されます。
ロックを正しく使用すると、同時実行時のデータのセキュリティを確保できますが、同時実行の程度が高くなく、競争が激しくない場合、ロックの取得と解放はパフォーマンスを無駄に浪費することになります。この場合、CAS を使用してデータのセキュリティを確保し、ロックフリーのプログラミングを実現することを検討できます。
共有変数の安全な操作を確保するための CAS の原理はすでに理解しています。 , しかし、上記のCAS 動作にも不備があります。現在のスレッドがアクセスする共有変数の値が A であるとします。スレッド 1 が共有変数にアクセスするプロセス中に、スレッド 2 は共有変数を操作して B に代入します。スレッド 2 は独自のロジックを処理した後、 A への共有変数。このとき、スレッド 1 は共有変数の値 A と元の値 A を比較し、他のスレッドが共有変数を操作していないと誤って操作し、操作の成功をそのまま返します。これがABAの問題です。ほとんどの企業では、共有変数に他の変更があったかどうかを気にする必要はありませんが、元の値が現在の値と一致している限り、正しい結果を取得できます。共有変数の結果は変更されていないことと同等ですが、プロセス内の他のスレッドによって共有変数が変更されることも許容されません。幸いなことに、ABA 問題には成熟した解決策があり、シェア変数にバージョン番号を追加すると、シェア変数が変更されるたびにバージョン番号の値が自動的に増加します。 CAS 操作では、比較するのは元の変数値ではなく、共有変数のバージョン番号です。シェア変数操作ごとに更新されるバージョン番号は一意であるため、ABA の問題を回避できます。
まず第一に、複数のスレッドが通常の変数に対して同時操作を実行することは安全ではありません。1 つのスレッドの操作結果は、他のスレッドによって使用される可能性があります。上書きします。たとえば、今 2 つのスレッドを使用しています。各スレッドは、初期値 1 の共有変数を 1 つずつ増やします。同期メカニズムがない場合、共有変数の結果は小さくなる可能性があります。 3よりも。つまり、スレッド 1 とスレッド 2 の両方が初期値 1 を読み取り、スレッド 1 がそれを 2 に割り当て、スレッド 2 がその値が配置されているメモリから読み取った値は変更されないままである可能性があります。スレッド 2 も変数を増加します。 1 を加えて 2 に割り当てるため、最終結果は 2 となり、予想される結果の 3 よりも小さくなります。インクリメント操作はアトミック操作ではないため、共有変数操作の安全でない問題が発生します。この問題を解決するために、JDK は対応するアトミック操作を提供する一連のアトミック クラスを提供します。以下は、AtomicInteger の getAndIncrement メソッドのソース コードです。ソース コードを見て、CAS を使用してスレッドセーフな整数変数のアトミック加算を実装する方法を見てみましょう。
rreegetAndIncrement が実際に UnSafe クラスの getAndAddInt メソッドを呼び出してアトミック操作を実装していることがわかります。以下は getAndAddInt のソース コードです。
<code>/**<br> * 原子性的将当前值增加1<br> *<br> * @return 返回自增前的值<br> */<br>public final int getAndIncrement() {<br> return unsafe.getAndAddInt(this, valueOffset, 1);<br>}<br></code>
私たちは皆、再入可能ロック ReentrantLock などのロックについてよく知っています。JDK によって提供されるさまざまなロックは、基本的に AbstractQueuedSynchronizer クラスに依存しています。複数のスレッドがロックを取得しようとすると、それらはキューに入って待機します (マルチスレッドも含む)。 -スレッド エンキュー操作。アトミック性は CAS によって保証されています。ソース コードは次のとおりです:
<code>/**<br> * 原子的将给定值与目标字变量相加并重新赋值给目标变量<br> *<br> * @param o 要更新的变量所在的对象<br> * @param offset 变量字段的内存偏移值<br> * @param delta 要增加的数字值<br> * @return 更改前的原始值<br> * @since 1.8<br> */<br>public final int getAndAddInt(Object o, long offset, int delta) {<br> int v;<br> do {<br> // 获取当前目标目标变量值<br> v = getIntVolatile(o, offset);<br> // 这句代码是关键, 自旋保证相加操作一定成功<br> // 如果不成功继续运行上一句代码, 获取被其他<br> // 线程抢先修改的变量值, 在新值基础上尝试相加<br> // 操作, 保证了相加操作的原子性<br> } while (!compareAndSwapInt(o, offset, v, v + delta));<br> return v;<br>}<br></code>
Uusafe クラスによって提供されるさまざまなアトミック操作に加えて、 JDK では、実際に開発中に CAS のアイデアを使用して、同時条件下での安全なデータベース操作を確保できます。 ユーザー テーブルの構造とデータが次のとおりであると仮定します。バージョン フィールドは、オプティミスティック ロックを実装するためのキーです。
id | user | coupon_num | version |
---|---|---|---|
1 | 朱暁明 | 0 | 0 |
以上がCAS と Java オプティミスティック ロックの使用方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。