Javaによって実装された同時操作は、CPUによって最終的に完了する必要があることを知っています。それまでの間、Javaソースコードを。クラスファイルにコンパイルし、ロードしてから、アセンブリ言語として解釈された仮想マシン実行エンジンによって実行され、オペレーティングシステムの命令に変換され、1、0に変換され、最後にCPUが認識され、実行されます。
Javaの同時性について言及するとき、Javaの一般的なキーワードを考えずにはいられません。次に、これら2つのシャットダウンワードからそれらを分析します。
揮発性の基礎となる実装原則
同期の実装原則とアプリケーション
揮発性
不安定性といえば、インタビュアーはJavaのインタビューで尋ねるお気に入りの質問です。私たちがそれを見るとき、私たちが考える最初のことは、スレッド間の視界を維持することです。これは、同期した軽量の同期であり、場合によっては同期したものを置き換えることができます。
揮発性の役割:
揮発性によって変更された変数の場合、Javaメモリモデルは、すべてのスレッドで見られる変数値が一貫していることを保証します。
揮発性の仕組み:
揮発性変数を定義し、値を割り当て、ツールを使用してJITコンパイラによって生成されたアセンブリ命令を取得できます。揮発性変数に書き込むと、追加の命令があります。ロックが付いている命令:
ロックプレフィックス命令により、マルチコアプロセッサに2つのものが戻ります。
current現在のプロセッサキャッシュラインのデータをメモリに書き戻します。
この書き込みメモリ操作は、他のCPUのデータキャッシュされたメモリアドレスを無効にします。
上記の2つのポイントを知っている場合、Volatie変数のメカニズムを理解することは難しくありません。
複数のプロセッサでは、各プロセッサのキャッシュが一貫していることを確認するために、キャッシュの一貫性プロトコルが実装されます。各プロセッサは、バスで伝播されたデータを嗅ぎ、キャッシュされた値の有効期限が切れているかどうかを確認します。
同期
マルチスレッドの並行性を考えるとき、私が最初に考えることは同期されます。同期として翻訳。私たちは皆、それがヘビー級のロックであることを知っています。メソッドまたはコードブロックに使用すると、スレッドがこのロックを取得すると、他のスレッドが吊り下げられた状態に分類され、Javaの睡眠状態に表示されます。スレッドのサスペンションと実行時間は、オペレーティングシステムのカーネル状態(カーネル状態に対応するユーザー状態)に転送する必要があることを知っています。これは特にCPUリソースを無駄にします。
ただし、Java SE 1.6の後、Javaメンテナンスチームは一連の最適化を実行しました(これらの最適化は1つずつ議論されています)したがって、それは「重い」ものではなく、過去に利点があったReentrant Lockはそれほど有利ではありません(ReintrantLock)。
次の側面で同期したことについて話しましょう。
同期を実現するために同期された基本
同期された道具がどのようにロックされていますか
ポジティブロック、軽量ロック(スピンロック)、ヘビー級ロック
アップグレードをロックします
Javaで原子動作を実装する方法
synchronizationを達成するために同期された基本:
Hashtable、StringBuilder、その他の場所など、開発やJavaソースコードで同期していることがわかります。 2つの一般的な方法があります。
ⅰ、同期方法
同期メソッドは、メソッドの前に同期する必要があります。 1つのスレッドが実行されると、ロックが解放されるまで他のスレッドが待機します。メソッドの使用は、通常の同期方法と静的方法の2つのタイプに分けることができます。それらの違いは、ロックされたオブジェクトが異なることです。通常の方法のロックされた位置は現在のオブジェクトであり、静的メソッドのロックされた位置は現在のクラスのクラスオブジェクトです。
ⅱ、同期メソッドブロック
同期メソッドブロックは、同期後にブラケットに構成されたオブジェクトをロックします。このオブジェクトは、値と任意の変数またはオブジェクトにすることができます。
synchンク実装ロック:
JVM仕様では、JVMで同期した実装原則を確認できます。 JVMは、モニターオブジェクトの入力と終了に基づいて、同期メソッドとコードブロックの同期を実装します。コードブロックは、MonitorEnterおよびMonitorexit命令を使用して実装されます。同期方法は、JVM仕様に特に示されていません。ただし、特定の原則は異なるはずだと思います。 Javaソースコードをクラスファイルにコンパイルし、クラスByteCodeファイルの同期メソッドをマークすることにすぎません。この方法は、ByteCodeエンジンがこのメソッドを実行するときに同期されます。
deflectionロック、軽量ロック(スピンロック)、ヘビー級ロック:
ロックについて話す前に、JavaオブジェクトヘッダーとJavaオブジェクトヘッダーを知る必要があります。
同期によって使用されるロックは、Javaオブジェクトヘッダーに保存されます。 Javaオブジェクトヘッダーには、32ビット/64ビット(オペレーティングシステムのビット数に応じて)長さのマークワードは、ハッシュコードとオブジェクトのロック情報を保存します。 MarkWordには、それぞれ軽量ロック、バイアスロック、ヘビー級ロック、GCマークを表すロック00、01、10、11の状態を表す2ビットスペースがあります。
ポジティブロック:ポジティブロックはエキセントリックロックと呼ばれます。名前から、特定のスレッドに向かう傾向があるロックであることがわかります。
実際の開発では、マルチスレッドの並行性、ほとんどの同期方法は同じスレッドによって実行されることがわかりました。1つの方法を競う複数のスレッドの確率は比較的低いため、繰り返しの獲得とロックのリリースは多くのリソース無駄を引き起こします。したがって、スレッドに低コストでロックを取得するために、バイアスロックが導入されます。スレッドが同期ブロックにアクセスしてロックを取得すると、バイアスロックのスレッドIDは、オブジェクトヘッダーとスレッドのスタックフレームのロックレコードに保存されます。将来的には、スレッドが同期ブロックを入力して終了すると、ロックおよびロックを解除するためにCAS操作を実行する必要はありません。オブジェクトヘッダーの現在のマークワードを指すバイアスロックがあるかどうかを単純に確認する必要があります(MarkWordでは、現在のオブジェクトがバイアスロックをサポートするかどうかを示すバイアスロックフラグビットがあります。JVMパラメーターを使用してバイアスロックを設定できます)。
バイアスロックの放出に関して、バイアスロックは、競合が存在するまでロックを放出するメカニズムを使用しているため、バイアスロックを保持するスレッドは、他のスレッドがバイアスロックを競い合うときにロックを放出します。
注:Java6、7では、バイアスロックがデフォルトで開始されます
軽量ロック:
軽量ロックは、同期ブロックを実行する前に、JVMが現在のスレッドのスタックフレームにロックレコードを保存するためのスペースを作成し、オブジェクトヘッダーのマークワードをコピーすることです。次に、スレッドはオブジェクトヘッダーのマークワードをロックレコードへのポインターに置き換えようとします。成功した場合、現在のスレッドはロックを取得します。失敗した場合、それは他のスレッドがロックを競い合い、現在のスレッドが回転してロックを取得することを意味します。
④ロックアップグレード:
現在のスレッドが上記の方法を試してロックを取得できない場合、現在のロックが競合し、ロックがヘビー級ロックにアップグレードされることを意味します。
軽量ロックとバイアスロックの違い:
軽量ロックはCAS操作を使用して競争なしで同期で使用されるミューテックスを排除しますが、バイアスロックは競争なしで競争なしで同期全体を除去し、CASの操作さえも行われません!
javaで原子操作を実装する方法:
Javaが原子動作をどのように実装するかを理解する前に、プロセッサが原子運用をどのように実装するかを知る必要があります。
プロセッサは一般に、原子動作を実行する2つの方法に分割されます:キャッシュロックとバスロックの中でキャッシュロックが優れていますが、バスのロックはリソースを消費します。 (ここでは2つのロック方法についてはあまり説明しませんが、オペレーティングシステムには詳細な説明があります)
Javaは(ほとんど)ループCASを使用して原子動作を実装しますが、CASを使用して原子操作を実装すると、次の古典的な問題の一部も引き起こします。
1)ABAの問題
AtomicStampedReferenceクラスはJDKで解決するために提供されます(予想される参照と予想フラグのチェックを提供)
2)長いサイクル時間と高いオーバーヘッド
これを解決することはできません、これは循環の一般的な問題です
3)共有変数の原子動作のみを保証できます
問題を解決するためにJDKで原子課程が提供され、CAS運用のクラスに複数の共有変数を配置します。