blog

JAVA並行処理(4)アトミック演算の実装原理

アトミックとは「それ以上分割できない最小の粒子」を意味し、アトミック操作とは「中断できない操作または一連の操作」を意味します。 プロセッサは、複雑なメモリ操作のアトミック性を保証するために、バスロック...

Oct 6, 2020 · 5 min. read
シェア

アトムとは「それ以上分割できない最小の粒子」、アトミック・オペレーションとは「中断できない操作または一連の操作」という意味です。

アトミック演算のプロセッサ実装

プロセッサは、複雑なメモリ操作の原子性を保証するために、バス・ロックとキャッシュ・ロックの両方のメカニズムを提供します。

原子性を確保するためのバスの使用

複数のプロセッサが同時に共有変数の読み取り、書き換え、または書き込みを行うと、共有は同時に複数のプロセッサによって操作されるため、読み取り、書き換え、または書き込み操作はアトミックではなく、共有変数の値は操作後に期待される値と矛盾します。

例:i + +は、i = 1の場合、2つのi + +の操作は、期待される結果は3ですが、結果は2かもしれません。理由は、それぞれのキャッシュから同時に複数のプロセッサがiを、それぞれ、プラス1の操作を読み取り、その後、それぞれシステムメモリに書き込まれる可能性があり、その後、アトミックのその読み取り書き換え共有操作を確保したい、CPU1が共有変数を書き換えることを確認する必要がありますCPU2ができない場合CPU1は、共有変数の読み取りまたは書き込み時にCPU2は、共有変数のキャッシュメモリアドレスを操作することはできません。

プロセッサは、バスロックを使用することでこの問題を解決します。バスロックは、プロセッサが提供するLOCK#信号を使用します。 プロセッサがこの信号をバス上に出力すると、他のプロセッサからの要求はブロックされ、プロセッサは共有メモリを排他的に使用できるようになります。

原子性を確保するためのキャッシュ・ロックの使用

同時に、メモリアドレスに対する操作はアトミックであることを保証すればよいのですが、バスロックはCPUとメモリ間の通信をロックするため、ロック期間中に他のプロセッサが他のメモリアドレスのデータを操作することができなくなり、バスロックの方がオーバーヘッドになるため、現在プロセッサはバスロックの代わりにキャッシュロックを使用して最適化されている場面もあります。

キャッシュ・ロックとは、あるメモリ領域がプロセッサのキャッシュ・ラインにキャッシュされ、ロック・オペレーション中にロックされた場合、プロセッサがメモリに書き戻すロック・オペレーションを実行すると、プロセッサはバス上でLOCK#信号をアサートせず、代わりに内部メモリ・アドレスを変更し、キャッシュ・コヒーレンス・メカニズムがオペレーションの原子性を保証できるようにすることを意味します。なぜなら、キャッシュ・コヒーレンシ・メカニズムは、2つ以上のプロセッサがキャッシュしているメモリ領域のデータを同時に変更することを防ぎ、他のプロセッサがロックされているキャッシュ・ラインのデータを書き戻したときにキャッシュ・ラインを無効にするからです。図2-3に示す例では、CPU1がキャッシュ・ラインのiを変更するときにキャッシュ・ロックを使用すると、CPU2は同時にiのキャッシュ・ラインをキャッシュすることができません。

しかし、プロセッサがキャッシュ・ロックを使用しない状況が2つあります:

  • 1つ目は、プロセッサ内部で操作のデータをキャッシュできない場合、または操作のデータが複数のキャッシュラインにまたがる場合です。
  • 2つ目は、キャッシュ・ロックをサポートしていないプロセッサがある場合です。インテル486とPentiumプロセッサの場合、ロックされたメモリ領域がプロセッサのキャッシュ・ラインにあっても、バス・ロックが呼び出されます。

アトミック演算のJAVA実装

アトミック演算は、ロックとサイクリックCASによってJAVAに実装することができます

巡回CASによるアトミック操作

CASはCompareAndSwap、比較と置換です。

VMのCAS演算は、プロセッサが提供するCMPXCHG(Compare and Exchange)命令を使用して実装されます。スピンCASの実装の基本的な考え方は、CAS演算が成功するまでループすることです。

Java 1.5以降、JDKの同時実行パッケージには、AtomicBoolean、AtomicInteger、AtomicLongなどのアトミック演算を実装するクラスが用意されています。これらのアトミック・ラッパークラスは、アトミックな方法で現在値を1だけ自己インクリメントしたり、1だけデクリメントしたりする便利なユーティリティメソッドも提供します。

アトミック演算のCAS実装に関する3つの主な問題

Javaの並行処理パッケージには、スピンCASを使用してアトミック操作を実装する並行処理フレームワークもあります。 CASはアトミック操作に対して非常に効率的なソリューションですが、CASにはABA問題、長いループ時間と高いオーバーヘッド、アトミック操作が1つの共有変数に対してのみ保証されるという3つの大きな問題があります。

ABA

ABA問題は、バージョン番号を変数に付加することで解決します。バージョン番号を付加する変数の前で、変数がバージョン番号プラス1に更新されるたびに、A→B→Aは1A→2B→3Aになります。Java 1.5以降、JDK AtomicパッケージはAtomicStampedReferenceクラスを提供し、ABA問題を解決します。このクラスのcompareAndSetメソッドの機能は、まず現在の参照が期待される参照と等しいかどうかをチェックし、現在のフラグが期待されるフラグと等しいかどうかをチェックします。

長いループ時間と高いオーバーヘッド

スピンCASが長時間失敗すると、CPUに非常に大きな実行オーバーヘッドを課すことになります。JVMがプロセッサが提供する一時停止命令をサポートすることができれば、効率はある程度改善されます。一時停止命令には2つの効果があります:第一に、CPUがあまりにも多くの実行リソースを消費しないように、命令のパイプライン実行を遅延させることができ、遅延時間は遅延時間のバージョンの特定の実装に依存し、いくつかのプロセッサではゼロです。第二に、メモリ順序の衝突によるループの終了時にCPUのパイプラインが空になるのを避けることができ、CPUの実行効率が向上します。

共有変数に対するアトミック操作は1回のみ保証

共有変数に対して操作を行う場合、サイクリックCASを使用すればアトミックな操作が保証されますが、複数の共有変数に対して操作を行う場合、サイクリックCASではアトミック性が保証されないため、この時はロックを使用します。もう一つ厄介なのは、複数の共有変数を一つの共有変数にまとめて操作する方法です。例えば、2つの共有変数i = 2, j = aがあり、ij = 2aを結合し、CASを使ってijを操作します。Java 1.5以降では、参照オブジェクト間のアトミック性を保証するAtomicReferenceクラスがJDKに用意されているので、1つのオブジェクトに複数の変数を入れてCAS操作を行うことができます。

ロック機構を利用したアトミック操作

ロック機構は、ロックを取得したスレッドだけがロックされたメモリ領域を操作できるようにします。JVMは、バイアスロック、軽量ロック、相互排他ロックなど、多くの種類のロック機構を内部的に実装しています。バイアス・ロックに加えて、JVMはサイクリックCASを使用したロックを実装しています。つまり、スレッドが同期ブロックに入りたいときは、サイクリックCASを使用してロックを取得し、同期ブロックから出るときは、サイクリックCASを使用してロックを解放します。

Study Source Javaにおける並行プログラミングの技術

個人的な意見です。議論へようこそ、個人ブログ

JAVAにおける並行処理プログラミングの課題

JAVA並行同期と揮発性の基本的な実装原理

JAVA並行ロック状態

JAVA並行アトミック操作の実装原理

JAVA 同時実行の前に起こること

Read next

OAシステムからオープンソースのフリーOAシステムまで、何を知る必要があるのか?

オフィスオートメーションは、コンピュータ、通信、および伝統的なオフィスに他の近代的な技術の使用であり、その後、オフィスの新しいタイプを形成。 近代的な機器や情報技術の使用は、情報資源を達成するために、オフィススタッフのマニュアルまたは反復的なビジネス活動、オフィスのトランザクションおよびビジネス情報の高品質で効率的な処理の伝統的な部分の代わりに、オフィスの自動化を達成するために...

Oct 6, 2020 · 3 min read

Vueの基本 - 共通機能

Oct 6, 2020 · 20 min read

SQL最適化のポイント

Oct 5, 2020 · 3 min read

ハッシュテーブル

Oct 5, 2020 · 5 min read

Jetpack MotionLayout

Oct 5, 2020 · 12 min read