JVM仮想マシン、スレッドセーフとロックの最適化、JVMの原則、診断、チューニングの深い理解

1.スレッドセーフ
スレッドセーフな定義:環境の下で、実行時にこれらの交互スレッドのスケジューリングと実行を考慮していないと、追加の同期、または呼び出しの他の方法を必要としない場合、複数のスレッドは、オブジェクトにアクセス正しい結果を得ることができ、オブジェクトの振る舞いを呼び出して、操作を調整し、オブジェクトはスレッドセーフです。

スレッドセーフ1.1 Java言語
ここで説明するには、それが複数のスレッド間でこの前提の共有データへのアクセスの有無に限定され、スレッドセーフです。

弱い強いから選別スレッドセーフ「のセキュリティのレベルを」よると、それぞれがJavaで動作することができ、次の5つのカテゴリにデータを共有します:

不変
絶対セキュリティスレッド
に対してスレッドセーフ
スレッド互換
ライン敵対
1不変
不変オブジェクト(不変)は、スレッドセーフでなければなりません。

共有データは、基本データ型である場合、最終的なキーワードは、定義に変更することができないことを確実にするために修飾することができます。

共有データがオブジェクトである場合、そのオブジェクトの振る舞いは、そのジョブのステータスに影響を及ぼさないことを確認する必要があります。最も簡単なものは不変であり、コンストラクタ、終了後のように、状態変数は、最終で宣言され供することです。

Java APIのタイプ不変満たす要件:java.lang.Stringで/ java.lang.Number型サブの一部などが挙げられます。

2.絶対スレッド安全性は
絶対的なスレッドの安全性は、スレッドセーフ満たすように定義されています。JavaのAPIは、彼らはほとんどの時間は、絶対的なスレッドセーフではありません、スレッドセーフなクラスであることを指摘しました。

3.相対的な安全性スレッド
相対的なスレッドの安全性は、スレッドの安全性について話して私たちの通常の意味で、それはこのオブジェクトの個別のスレッド安全な動作を確保する必要があり、我々は呼び出すときに、追加の保護手段が必要ですが、いくつかの特定の順序のためにはありません連続通話は、あなたが電話を決定するために、正の端を確保するための呼び出しを同期するために追加的な手段を使用する必要があります。

Javaは、スレッドセーフなクラスのほとんどはこのタイプ、などの包装などのベクトル/ハッシュテーブル/ synchronizedCollectionのコレクション()収集方法です。

4.スレッド互換の
スレッド互換オブジェクト自体はスレッドセーフではありませんが、同期の適切な使用は、同時実行環境内のオブジェクトの安全な使用を確保するために意味を呼び出すことによって終了することを意味します。ほとんどのJava APIクラスはスレッド互換性があります。

スレッド敵対的には
関係なく、同期がマルチスレッド環境で同時に使用することができない措置を講じているかどうかの端を呼び出すコードを指します。多くの場合、有害と避けるべきです。

スレッド敵対的なクラスの例は、スレッドのサスペンド()と並行して、それから、かどうかに関係なくコールの場合は、同時に2つのスレッドが、スレッドを中断しようとすると、スレッドを再開するために別の試みをスレッドオブジェクトを保持している場合、()メソッドを再開しています一時停止()中断スレッドが(再開を実行しようとしている場合は、そのスレッドの同期、ターゲットスレッドが)、デッドロックのリスクがあった、それは確かにデッドロックになります。これは、このような理由により、これらの2つの方法は、JDKを放棄されています。

1.2スレッドセーフな実装
1.2.1同期相互排除
ミューテックス同期(相互排他&同期)は、支持同時妥当性の一般的な手段です。同期スレッド(セマフォを使用する場合、またはいくつかの)同じ時間だけでその共有データを保証するために、複数のスレッドで共有データへの同時アクセスするための手段。ミューテックスは、クリティカルエリア(クリティカルセクション)、ミューテックス(ミューテックス)とセマフォ(セマフォ)されている相互に排他的な達成するための主な方法を同期させる手段です。したがって、ミューテックスは、同期化の結果であるため、単語、排他的な方法、同期目的。

synchronizedキーワード
のJava、相互排他同期手段の最も基本的ではキーワードを同期しています

原則synchronizedキーワード:

同期キーワードをコンパイルした後、2つのmonitorenterとmonitorexitバイトコード命令を形成するロック及びアンロックするために参照オブジェクトを示すために2バイト・コード・タイプ・パラメータを必要とする同期コードブロックの前後です。同期手順オブジェクトパラメータは、オブジェクト参照することを指定する場合、指定されていない場合、それは同期クラスメソッドまたはインスタンスメソッド、ロックオブジェクトとしてオブジェクトをフェッチまたはクラスに対応するオブジェクト・インスタンスに応じて変更されます。
仮想マシンの仕様は、monitorenter命令の実装では、最初の買収ターゲットをロックしてみてください。オブジェクトがロックされていないか、現在のスレッドがすでにそのオブジェクトのロックを所有している、ロックカウンタが1つインクリメントされている場合は1で、ロックカウントをmonitorexitを実行するときにロックカウンタが0であれば、ロックが解除されます。取得対象のロックが失敗した場合、現在のスレッドが待機をブロックします。
あなたがブロックするか、スレッドをウェイクアップした場合、Javaスレッドは、オペレーティングシステムのネイティブスレッドにマッピングされて、あなたはカーネルモードために、ユーザーモードからの変換を必要とする、支援するオペレーティング・システムを必要とするので、状態遷移は、フロックをとりますCPUのプロセッサ時間。仮想マシン自体が頻繁にカーネルモードに切り避けるため、ブロックされたスレッドに加えて、いくつかのスピン・ウェイト・プロセスを通知する前に、オペレーティングシステムなど、いくつかの最適化を、行いますながらだから、synchronizedキーワードは、ヘビー級のロックです。

同期を達成するリエントラントロック(ReentrantLockの)は
(ReentrantLockの)コードは、上記ミューテックスAPIレベルの性能、元の文法学生ミューテックスの性能の別のレベルに書き込まれる異なるリエントラントロックあろう。

ReentrantLockのは、いくつかの高度な機能を追加します。

割り込み待機:スレッドが保持しているロックは、スレッドが他のものに対処する代わりに待ってあきらめることを選択することができます待って、長い期間のためのロックを解除すると、それは長時間行うブロック同期プロセスを支援します。
フェアロック:複数のスレッドが同じロックを待って、ロックが年代順のアプリケーションのロックに順次取得しなければならない、むしろ公正なロックよりもロックが解除されたときに、これを保証する、のいずれかのロックスレッドがロックへのアクセス権を持っているのを待ちません。非同期ロック公正でReentrantLockのの、公正もデフォルト以外の、ロックはコンストラクタ公正な要件によって使用することができます。
複数の条件結合:条件に結合することができる複数のReentrantLockのオブジェクトが同時にオブジェクトとで同期、wait()を隠した状態で実施することができるロックオブジェクトの)((通知)またはのnotifyAllとIFつ以上の条件が関連付けられたとき、あなたは余分なロックを追加する必要があり、することができ、これは、単にnewCondition時間を呼び出す必要がReentrantLockの()はありません。
1.2.2ノンブロッキング同期
ミューテックス同期主な問題は、パフォーマンスの問題のスレッドブロックとウェイクアップがもたらしたということですので、これはまた、(同期ブロック)同期ブロックの同期として知られています。問題に対処するための方法では、相互排他同期は悲観的同時方式に属し、彼らはいつもかかわらず、あるデータを、共有する競争実際にそこにいるかどうかの右の同期の措置を(例えばロックなど)、そして、問題があるだろう行うにはないと思いますロック(ここで説明する概念モデルは、仮想マシンの最適化、不要なロックの大部分、実際には、ある)、ユーザーモードカーネルモードの変換、メンテナンスロックカウンタとブロックされたスレッドをチェックするためには、ウェイクアップ動作を待つ必要があります。

共有データの競合は、生成しない場合は、共有データには、他のスレッドの競合は、操作が成功したことならば、ある楽観的同時方式に基づく衝突検出、高度な操作:ハードウェアの命令セットの発展に伴い、我々は別のオプションを持っています競合が、それは、スレッドを中断する必要はありません。これを達成するために、他の代償措置(最も一般的な救済は、それが成功するまで再試行を継続することがある)、多くの楽観的同時方式を取るので、この同期は言いましたノンブロッキング同期(同期非ブロッキング)。

私たちが動作する必要があり、2段階の衝突検出原子を含み、ハードウェアによって達成することができ、ハードウェア保証セマンティック行動から行うことができ、プロセッサ命令による操作の唯一の複数の、このような従来の実行を要求しているようです彼らは以下のとおりです。

テストとセット(テストおよびセット)。
入手して増加(フェッチおよびインクリメント)。
スワップ(スワップ)。
比較およびスワップ(比較とスワップ、以下CASという)。
負荷結合/ストア条件(以下、LL / SCと呼ぶ/ストア条件リンク負荷)。
1.2.3ませ同期スキーム
スレッドセーフを確実にするには、必ずしも同期されません、と2の間には因果関係がありません。同期は、この方法は、正確な位置決めを確実にする任意の同期手段なし、当然、データの共有を含むべきではない場合、共有データ競合の正確さを保証する唯一の手段です。だから、本質的にスレッドセーフであるいくつかのコードがあるでしょう。二つのカテゴリ:

再入可能コード(再入可能コード)であってもよい:このコードは、コントロールしながら、任意のタイムコードの実行で中断することができる、純粋なコード(ピュアコード)、(自身への再帰呼び出しを含む)別のコードを実行するためにオフと呼ばれています右の復帰後、任意のエラーは、元のプログラムが表示されません。すべてのリエントラントコードはスレッドセーフです。リエントラントコードは、例えば、ヒープに格納されたデータに依存せず、共通のシステムリソースは、メソッドが呼び出されないことにより、渡された状態パラメータの量で使用されるなど、非リエントラント、いくつかの共通の特徴を有していてもよいです。リエントラント単純な原理は、コードを含む分析:結果を返すメソッドがあれば、同一のデータが入力されるように、それは同じ結果を返すことができ、予測可能である場合、それはもちろんスレッドの再入の要件を満たすことができますセキュリティ。
ローカルストレージをスレッド(スレッドローカルストレージ):必要なデータのコードの一部は、他のコードと共有する必要がある場合は、これらの共有データのコードが同じスレッドで実施を確保するかどうかを見て?あなたは競合の問題のないデータの同期は、スレッドの前に発生していないことを確認できるように、彼らは、共有データを置くことができるならば、視認性の範囲は、同じスレッドに制限されています。
このアプリケーションは、特性に準拠しています(例えば、「プロデューサー-消費者」としてモード)建築のパターンを使用してほとんどのコンシューマ・キューのスレッドで行わできるだけ多くの消費消費過程を話しますが、各サービス要求のための1つ」の古典的なウェブインタラクションモデルをスレッド「(スレッドあたりの要求)のアプローチ、広く使われている。このアプローチは、Webサーバーアプリケーションの多くは、スレッドの安全性の問題を解決するために、スレッドローカルストレージを使用することができます。

変数が複数のスレッドによってアクセスされる場合Javaは、あなたは「変数」としてそれを宣言するために揮発性のキーワードを使用することができます。変数がスレッドに排他的である場合、スレッドローカルストレージはjava.lang.ThreadLocalクラスによって達成することができます機能。各スレッドのThreadオブジェクトを簡単ThreadLocal.threadLocalHashCodeのセットを格納ThreadLcoalMapオブジェクトはKVキーと値のペアの価値スレッドローカル変数への鍵となりました、ThreadLocalのオブジェクトはそれぞれ、現在のスレッドThreadLocalMapアクセスエントリですThreadLocalのオブジェクトは、この値は、スレッドKV変数鍵ペアの対応するネイティブスレッドを使用して取得することができるユニークなthreadLocalHashCode値を含みます。

2.ロックの最適化
のHotSpot仮想マシンの開発チームは、このような適応的なスピンロックの除去、ロック粗大化、軽量ロック、ロックバイアスなど、さまざまなロックの最適化技術を実装するために、このバージョンでは多くのエネルギーを費やしました。

2.1スピンロック
の問題を解決するために:パフォーマンス上のミューテックス同期最大の影響が遮断され達成され、ハングスレッドおよびリカバリ操作は、システムの同時パフォーマンスにこれらの操作を完了するために、カーネルモードに通す必要があり、非常に提起します圧力。

解決策:スレッドを待機させるために、我々は、スレッドがループ(スピン)を実行忙しい取得する必要があり、この技術は、スピンロックです

、ただしスレッド切り替えのオーバーヘッドなしに、時間そのものを待って、スピンが、それはプロセッサを集中することで、ロックが占有されている場合は非常に短い時間、スピン、代わりに、ブロッキングのスピンのために待つことができない、と私は、プロセッサ要件の量の話ではないだろうほかの不要なパフォーマンスを持って、任意の有用な作業を行うことなく、逆に、効果のためのプロセッサリソースの無駄な消費だけスピンスレッドを待ちます。スピンロックを取得していない、まだ数の上限を超えた場合そのため、スピン待機時間は、一定の制限を持っている必要があります、あなたはスレッドをハングアップする伝統的な方法を使用する必要があります。デフォルト値は、スピンの10倍の数であり、ユーザがパラメータを使用することができ-XX:PerBlockSpin修飾。

2.2適応スピンロック
問題解決スピンロック:システムが必要でないためにスピンのためには、それぞれの選択時間が固定されているロック。

JDK1.6は、適応スピンロックを導入しました。スピン時間は同じ時間スピンロックとロックの所有者に以前の状態によって決定されます。同じオブジェクトのロックをスピン待つだけで成功したロックを獲得し、ロックを保持しているスレッドが実行されている場合、仮想マシンはこのスピンでも成功する可能性があると思いますが、それは比較的スピン待ちのためにできるようになります長いです。ロックの場合は、スピン工程の後にロックを得ることに成功したまれスピンはプロセッサリソースの浪費を避けるために省略することができます。適応スピンでは、プログラムの実行中に、継続的にパフォーマンスモニタリング情報を改善し、仮想マシンプログラムのロック状態予測がより正確になり、仮想マシンは、より多くの「スマート」になります。

ロックの2.3撤廃は
時間コンパイラを実行している仮想マシンでは意味、コードはいくつかの同期を必要としますが、競争を排除するために、データを共有ロックが検出することができます。ロックそれはコードの一部で判断された場合、他のスレッドにアクセスできるように支援するためのデータの分析に基づいて判断から、メインの脱出を排除する、すべてのデータは、ヒープの外に逃れられないだろう、スタック上のデータとして扱うことが可能です彼らはスレッドプライベート、同期を必要とせずに自然にロックされています。

多くの措置は、Javaプログラムにおける同期コードの有病率は、私たちの想像を超えて、同期プログラマは追加されません。

パブリック(ストリングS1、S2ストリング、ストリングS3)をconcatString列{
S1 + S2 + S3を返す;
}
。1
2
。3
列は不変クラスであるので、文字列を連結し、常に新しい文字列オブジェクトを生成することによって行われます、したがって、あるJavacコンパイラが自動的に最適化された文字列の接続を行います。JDK1.5前に、対象のStringBufferはappend()オペレーション、JDK1.5に変換後、連続アペンドStringBuilderオブジェクト()オペレーションに変換されます。コードのセクションは次のようになりなることがあります。

ストリングconcatStringパブリック(ストリングS1、S2ストリング、ストリングS3){
StringBuilderのStringBuilderの新しい新しいSB =();
sb.append(S1);
sb.append(S2);
sb.append(S3);
)(sb.toStringを返します。
}
。1
2
。3
。4
。5
。6
。7
それぞれStringBuilder.Append()シンクブロックの両方で、オブジェクトロックがSBです。仮想マシンの観測変数のSBは、すぐにその動的な範囲は()の内部concatStringに限定されています。そこにロックがありますが、安全に除去することができますが、時間のコンパイル後に、このコードはすべて無視されますので、それは、concatString(に決して「エスケープ」)SBへのすべての参照で、他のスレッドがそれにアクセスすることはできません同期および直接実行。

2.4ロック粗大
我々はコードを書いたときに、いくつかのケースでは、常に推奨範囲はできるだけ小さくブロック制限を同期します-のみ共有データ同期の実際の範囲で行われ、ロックの競合がある場合は、ロックを待ちますスレッドも、できるだけ早くロックを得ることができます。

連続した一連の動作が繰り返しロックと同じオブジェクトのロックを解除している場合には、さらに同期動作は、ループ本体に存在し、どのスレッドの競合が存在しない場合であっても、頻繁にミューテックス同期が不要な性能をもたらすことができます損失。

連続アペンド(のコードセグメント)これはケースです。仮想マシンが同じオブジェクトに断片化されロックされているような一連の動作を検出する場合、同期コードの上記セクションの全体の動作シーケンスの外側に(粗面化処理)、例えば、第二に拡張されたときに、ロックが延長されます最後に追記されるまでのappend()の前に()の後に、これだけあなたは一度それをロックする必要があります。

2.5軽量ロック
の問題を解決するには:により、伝統的なヘビー級のオペレーティングシステムには、運転モードの把握をカーネルために、ユーザーモードから変更する必要があるすべてのロック、アンロックを同期させるために、ミューテックスのロックを使用するため、システム全体がいくつかをもたらしますパフォーマンスの消費量。

原則ロック軽量:
軽量かつ偏ったロックロックを理解するには、オブジェクトのHotSpot仮想マシン(オブジェクトヘッダ)メモリレイアウトから開始する必要があります。二つの部分、例えば、ハッシュコード(ハッシュコード)のようなオブジェクト自体の動作データを記憶する第1部分、GC世代年齢(GC年齢)など、32ビット・データのこの部分の長さに分割された情報のホットスポットVMオブジェクトヘッダと64ビットの仮想マシンは、正式に「マーク・ワード」として知られ、32ビットと64ビットです。オブジェクトタイプのデータ領域へのポインタを格納するために使用される方法の別の部分。32ビットの仮想マシン25bitハッシュコードで少しオブジェクト、4ビットのストレージオブジェクト世代年齢、2ビット記憶ロックフラグ、その他を格納するのに使用されます。

 

原則:

コードの同期ブロックに入ると、この同期オブジェクトが(ロックフラグが「01」の状態で)ロックされていない場合は、仮想マシンが最初に現在のスレッドのスタックフレームにロック(ロックレコード)と呼ばれるスペースを構築し、現在のマークWordがロックされたオブジェクトのコピーを保存するために(これの公式コピープラス変位プレフィックスを、すなわち変位マークのWord)
マークWordの仮想マシンがCAS操作オブジェクトはレコードのポインタをロックするように更新されて使用しようとします。更新アクションが成功した場合、スレッドは、このオブジェクトは軽量ロック状態であることを意味し、「00」に変換されるオブジェクトのロック、およびマークWordのロックフラグ(最後の2ビットマークのWord)のオブジェクトを所有します
この更新が失敗した場合は、仮想マシンが最初にそれ以外の場合はこれを説明する、唯一の現在のスレッドがすでにこのオブジェクトのロックを所有して説明するならば、それは直接継続して同期ブロックを入力することができ、現在のスレッドのスタックフレームへのマークのWordポイントの対象かどうかを確認しますロックオブジェクトは、他のスレッドによって横取りされています。
もはや有効で同じロック、軽量ロックの競合つ以上のスレッドが、ヘビー級のロックを拡張する場合には、フラグ値のロック状態は「10」となり、マーク・ワードはヘビー級を指し中に保存されていますブロックされた状態に入るべきであるロックを待機しているスレッドが続くロック(ミューテックス)ポインタ。

 

パフォーマンスを向上させるための手順に従い、軽量ロックは、経験的データである「ロックの大半については、全体の同期サイクル、には競争がないです」です。ミューテックスを使用してのオーバーヘッドなしCAS操作を使用して何の競争、軽量ロックが存在しない場合。以上の2つのスレッドがもはや有効で軽量、軽量ロックで競合をロックした場合、それはヘビー級のロックを拡大する場合には、ロックフラグの状態が変更されると、「10」

2.6偏ったロックは
、すべてのロック解除操作がCAS命令プリミティブを使用する必要がロックし、CASは競争せずに動作し、軽量ロックの使用:問題を解決します。この操作は、オブジェクトのみにアクセスするための一つのスレッドのためであり、それはいくつかのパフォーマンスのオーバーヘッドが発生します。

JDK1.6は、ロックに導入、最適化をロックし、さらにプログラムの動作性能を向上させるために、競争の不在下でのデータ同期プリミティブを排除するように設計されています。ロックは、軽量CASを使用する場合ミューテックス同期の使用を排除競争ケースの不在下で動作する、バイアスされたロックは、全体同期の場合における競争の不存在下で除去され、偶数CAS動作が行われないことです。

原則:

ロックオブジェクトは、最初のスレッドが取得している場合、仮想マシンは、対象ヘッダフラグが「01」に設定されているであろう、それはバイアスモードです。同時に、CASの動作を使用することは、ロック・スレッドIDは、操作が成功した場合、将来的にはあなたが関連するシンクブロックに入るたびにバイアスされ、ロックねじロックを保持して、仮想マシンができないCASの間でマークWordオブジェクトに記録されているに取得しますさらなる同期動作
他のスレッドがロックを取得しようとするが、それはエンドバイアスモードに来ました。ロックオブジェクトに応じて、現在、バックロック解除(フラグ「01」)又は軽量ロック(フラグ「00」)状態では、後続の同期操作に離脱バイアス(取り消しバイアス)の状態でロックされます上で紹介したように軽量ロックのように行われます


競争せずに同期してバイアスされたロックは、パフォーマンスを向上させることができます。また、最適化された効率(トレード・オフ)の特性とのトレードオフであるプログラムは、ロックのほとんどは、常に余分な状況をロックする傾向が異なるスレッドのアクセス数であれば、プログラムを実行するためには必ずしも有益ではありません。前提の下で、特定の問題を分析し、時にはパラメータを使用-XX:-UseBiasedLockingが、パフォーマンスを向上させることができ偏ったロックの最適化を禁止します。
----------------
免責事項:この記事はCSDNブロガー「MasterT-J」で、元記事では、CC 4.0 BY-SAの著作権契約、複製、オリジナルのソースとのリンクを添付してくださいに従ってくださいこの文。
オリジナルリンクします。https://blog.csdn.net/qq_21125183/article/details/85174651

おすすめ

転載: www.cnblogs.com/spark9988/p/11521567.html