スレッドセーフティとロックの最適化に関する主要な知識

スレッドセーフ

複数のスレッドがオブジェクトにアクセスする場合、ランタイム環境でこれらのスレッドのスケジューリングと代替実行を考慮する必要がない場合は、追加の同期や呼び出し元の他の調整された操作は必要ありません。このオブジェクトを呼び出す動作は次のとおりです。正しい結果が得られれば、このオブジェクトはスレッドセーフです。

Javaでのスレッドセーフティ

スレッドセーフティをより深く理解するために、ここではスレッドセーフティをtrueまたはfalseのいずれかのバイナリ除外オプションとして扱うことはできません。Java言語をスレッドセーフティの「安全度」に基づいて弱いものから強いものに並べ替えることができます。のさまざまな操作で共有されるデータは、次の5つのカテゴリに分類されます。

1.不変

Java言語(特にJDK1.5以降、つまりJavaメモリモデルが改訂された後のJava言語)では、オブジェクトのメソッド実装やメソッドの呼び出し元に関係なく、不変オブジェクトはスレッドセーフでなければなりません。スレッドの安全保護対策を講じる必要はありません。

共有データが基本データ型の場合、定義時にfinalキーワードで変更されていれば、不変であることが保証されます。共有データがオブジェクトである場合は、オブジェクトの動作がその状態に影響を与えないようにする必要があります。オブジェクトの動作が状態に影響しないようにするには、多くの方法があります。最も簡単な方法は、オブジェクトに状態を置くことです。変数はfinalとして宣言されます。例:java.lang.Stringクラス、java.lang.integerクラス

2.絶対的なスレッドセーフティ

自分自身をJava APIのスレッドセーフクラスとしてマークすると、それらのほとんどは絶対的なスレッドセーフではありません。

3.相対的なスレッドセーフティ

相対スレッドセーフとは、通常の意味でのスレッドセーフです。このオブジェクトの個々の操作がスレッドセーフであることを確認する必要があります。呼び出し時に追加の保護手段を講じる必要はありませんが、特定の順序で継続的に呼び出す場合は、呼び出しの正確さを保証するために、呼び出し側で追加の同期手段を使用する必要がある場合があります。例:Vector、HashTable、CollectiongsのSynchronizedCollection()メソッドでラップされたコレクションなど。

4.スレッド互換

スレッド互換とは、オブジェクト自体がスレッドセーフではないことを意味しますが、呼び出し側で同期メソッドを使用することにより、オブジェクトを並行環境で安全に使用できることを確認できます。通常、クラスはスレッドセーフではないと言います。これは事実です。

5.スレッドの反対

スレッドの反対は、呼び出し側が同期手段を使用しているかどうかに関係なく、マルチスレッド環境で同時に使用できないコードを指します。スレッドの反対の例は、Threadクラスのsuspend()およびresume()メソッドです。このため、jdkステートメントによって放棄されました。一般的なスレッドの反対の操作には、System.setIn、System.setOut、およびSystem.runFinalizersOnExit()があります。

スレッドセーフの実装方法

1.相互に排他的な同期

同期とは、複数のスレッドが同時に共有データにアクセスする場合、共有データが同時に1つ(またはセマフォを使用する場合はいくつか)のスレッドによってのみ使用されることを確認することです。相互排除は同期を達成するための手段であり、クリティカルセクション、クリティカルセクション、ミューテックス、およびセマフォは、相互排除の主な方法です。

同期キーワード

synchronizedキーワードがコンパイルされると、同期ブロックの前後に、monitorenterとmonitorexitの2つのバイトコード命令が形成されます。両方のバイトコードには、オブジェクトをロックおよびロック解除することを示すReferenceタイプのパラメーターが必要です。オブジェクトパラメータが指定されている場合、それはこのオブジェクトの参照です。明示的に指定されていない場合、対応するオブジェクトインスタンスまたはクラスオブジェクトは、変更がインスタンスメソッドであるかクラスメソッドであるかに応じて、ロックオブジェクトと見なされます。

仮想マシン仕様のmonitorenterとmonitorexitの動作の説明には、2つの注意点があります

  • 同期されたブロックは同じスレッドに対して再入可能であり、自分をロックする問題はありません
  • 入力されたスレッドが実行された後、同期ブロックは他のスレッドのエントリをブロックします。
    • Javaスレッドは、オペレーティングシステムのネイティブスレッドにマップされます。スレッドをブロックまたはウェイクアップするには、オペレーティングシステムが完了を支援する必要があります。これには、ユーザーモードからコアモードへの切り替えが必要なため、状態の切り替えはプロセッサ時間を消費します。
    • 単純な同期ブロックの場合、状態遷移によって消費される時間は、ユーザーコードの実行時間よりも長くなる可能性があります
    • 仮想マシン自体も、スレッドをブロックするようにオペレーティングシステムに通知する前にスピン待機プロセスを追加するなど、いくつかの最適化を実行して、コア状態への頻繁な切り替えを回避します

再入可能ロック

Synchornizedと比較して、ReentranLockは主に次の3つの項目を含むいくつかの高度な機能を追加します。

  • 待機は中断される可能性があります。現在ロックを保持しているスレッドが長時間ロックを解放しない場合、待機スレッドは待機をあきらめ、代わりに他のものを処理することを選択できます。
  • 公平なロック:複数のスレッドが同じロックを待機している場合、それらはロックを適用する時系列に従って順番にロックを取得する必要があります。同期されたロックは不公平です。ReentrantLockもデフォルトでは不公平ですが、ブール値で構築できます。機能には公平なロックが必要
  • 複数の条件をバインドするロック:ReentrantLockオブジェクトは複数のConditionオブジェクトを同時にバインドでき、同期して、ロックオブジェクトのwait()およびnotify()またはnotifyAll()メソッドは、必要に応じて冗長な暗黙の条件を実装できます条件付きアソシエーション。追加のロックを追加する必要があります
2.ノンブロッキング同期

ハードウェア命令セットの開発により、相互排除と同期に加えて、この「悲観的」な並行性戦略に別の選択肢があります。競合検出に基づく楽観的並行性戦略です。素人の言葉では、これは他のスレッドがない場合の最初の操作です。共有データの競合が発生した場合、操作は成功します。共有データの競合が競合した場合は、他の補正手段を講じます(最も一般的な補正手段は、成功がわかるまで再試行を続けることです)、この楽観的同時実行戦略の多くの実装ではスレッドを中断する必要がないため、この同期操作は非ブロッキング同期(非ブロッキング同期)と呼ばれます。

CASアトミック操作https : //blog.csdn.net/cringkong/article/details/80533917

3.同期スキームなし

スレッドの安全性を確保するために、同期する必要はありません。2つの間に因果関係はありません。

再入可能コード

この種のコードはPureコードとも呼ばれます。コードの実行時にいつでも中断され、代わりに別のコード(再帰的にそれ自体を呼び出すことを含む)を実行し、コントロールが戻った後、元のプログラムは表示されません。エラー。

再入可能コードには、ヒープに格納されたパブリックシステムリソースに依存しない、使用される状態の量がパラメーターによって渡される、非再入可能メソッドが呼び出されないなど、いくつかの共通の特性があります。単純な判断方法:メソッドの場合、結果が予測可能であり、同じデータを入力して同じ結果を返す限り、彼は再入可能性の要件を満たします。

スレッドローカルストレージ

データを共有するコードを同じスレッドで実行できることを保証できる場合は、このようにして、同期なしでスレッド間でデータの競合が発生しないようにすることができます。

これらの特性を満たすアプリケーションは珍しいことではありません。コンシューマーキューを使用するほとんどのアーキテクチャモデル(「プロデューサー-コンシューマー」モデルなど)は、1つのスレッドで可能な限り製品の消費プロセスについて話し、最も重要なものはアプリケーションの例です。これは、従来のWeb相互作用モデルにおける「1つのリクエストが1つのサーバースレッドに対応する」(Thread-per-Request)の処理方法です。この処理方法の幅広いアプリケーションにより、多くのWebサーバーアプリケーションはスレッドローカルストレージを使用してスレッドの安全性の問題を解決できます。

javaの変数はスレッドによって排他的に使用され、スレッドローカルストレージの機能はjava.lang.ThreadLocalクラスを介して実現できます。


ロックの最適化

1.スピンロックとアダプティブロック

スピンロック

物理マシンに複数のプロセッサがあり、同時に2つ以上のスレッドを並行して実行できる場合、ロックを要求したスレッドを「待機」させることができますが、プロセッサの実行時間をあきらめることはありません。ロックされたスレッドがすぐにロックを解放するかどうか。スレッドを待機させるには、スレッドにビジーループ(スピン)を実行させるだけでよくこの手法はスピンロックと呼ばれます

スピンロックはjdk 1.4.2で導入されましたが、デフォルトでオフになっています。-XX:+ UseSpinningパラメータを使用してオンにできます。jdk1.6では、デフォルトでオンになっています。

:スピンの待機時間には一定の制限があり、スピンが制限回数を超えてロックが正常に取得されない場合は、従来の方法でスレッドを中断する必要があります。スピン数のデフォルト値は10です。ユーザーはパラメーター-XX:PreBlockSpin使用して変更できます。

適応ロック

アダプティブスピンロックはjdk1.6で導入されました。適応とは、スピン時間が固定されなくなったことを意味しますが、同じロックでの以前のスピン時間とロック所有者の状態によって決定されます。

同じロックオブジェクト上で、スピン待機がロックを正常に取得し、ロックを保持しているスレッドが実行されている場合、仮想マシンはこのスピンも再び成功する可能性が高いと考え、スピンを許可します待機は、100サイクルなどの比較的長い時間続きます。さらに、特定のロックでスピンが正常に取得されることがめったにない場合は、将来このロックを取得するときにスピンプロセスを省略して、プロセッサリソースの浪費を防ぐことができます。

2.ロックの削除

ロックの排除とは、仮想マシンのリアルタイムコンパイラが実行中にコードの同期が必要であるにもかかわらず、共有データの競合を排除するためのロックがないことが検出されたことを意味します。

ロックの解消の主な判断は、エスケープ分析からのデータサポートに基づいています。コードの一部でヒープ上のすべてのデータがエスケープされず、他のスレッドからアクセスされないと判断された場合、それらはスタック上のデータとして扱うことができます。これらはスレッドプライベートであり、同期ロックは当然不要です。

3、锁粗化

原則として、コードを記述するときは常に、同期ブロックのスコープをできるだけ小さく制限することをお勧めします。これは、共有データの実際のスコープでのみ同期することです。これは、同期する必要がある操作の数をできるだけ可変にするためです。小さい、ロックの競合がある場合、待機中のスレッドはできるだけ早くロックを取得できます。

4.軽量ロック

軽量ロックは、jdk1.6で追加された新しいロックメカニズムです。名前の「軽量」は、オペレーティングシステムのミューテックスを使用して実装された従来のロックに関連しています。最初に強調すべきことは、軽量ロックは重量ロックの代わりに使用されないことであり、マルチスレッドの競合なしにオペレーティングシステムミューテックスを使用する従来の重量ロックのパフォーマンス消費を削減することを目的としています。 。

写真

写真

5.バイアスロック

写真

具体的な説明と理解については、https//blog.csdn.net/zq1994520/article/details/84175573を参照してください。

オリジナルの記事を8件公開 Like1 訪問数261

おすすめ

転載: blog.csdn.net/qq_40635011/article/details/105495705