ソフトウェアアーキテクチャ-分散シリーズ並行プログラミングロックロックとツールの制限

 マルチスレッドプログラミングは効率を大幅に向上させますが、特定の隠れた危険ももたらします。たとえば、2つのスレッドが一意のデータをデータベーステーブルに同時に挿入すると、同じデータがデータベースに挿入される可能性があります。今日は、スレッドセーフの問題と、スレッドセーフの問題を解決するためにJavaで提供されるメカニズムについて説明します。ソースコード:https//github.com/limingios/netFuture/blob/master/JSR133中国語版.pdf

ソフトウェアアーキテクチャ-分散シリーズ並行プログラミングロックロックとツールの制限

同期&揮発性

JSR133 http://www.cs.umd.edu/~pugh/java/memoryModel
FIFO(先入れ先出し)は、単に先入れ先出しを意味します。

  • オブジェクトロックとは

    オブジェクトロックは、メソッドロックとも呼ばれ、オブジェクトインスタンス用です。オブジェクトの特定のメモリ位置でのみ宣言して、オブジェクトにロックがあるかどうかを識別します。そのため、現在のオブジェクトのみをロックし、他のオブジェクトはロックしません。インスタンスは影響を及ぼし、同期によって変更された同じメソッドにアクセスするときに、異なるオブジェクトがブロックされることはありません。

  • 些細なことは何ですか

    クラスロックは、クラス全体をロックするためのものです。このクラスのオブジェクトを宣言するスレッドが複数ある場合、このクラスロックのオブジェクトが破棄されるか、クラスロックがアクティブに解放されるまで、ブロックされます。この時点で、ブロックされたスレッドはブロックされます。クラスのロックを保持し、クラスのオブジェクトを宣言するオブジェクトを作成します。他のスレッドは引き続きブロックされます。

(上記の百度)つまり、1つの文で、オブジェクトの数、オブジェクトの数に関係なく、複数のオブジェクトを共有し、1つしかない場合、どのように呼んでも同期されます

同期

同期キーワードの使用法を理解する前に、まず概念を見てみましょう。名前が示すように、相互排除ロック:相互排除アクセスの目的を達成できるロック。

1.簡単な例を挙げます。重要なリソースにミューテックスロックを追加した場合、スレッドが重要なリソースにアクセスすると、他のスレッドは待機することしかできません。
2. Javaでは、各オブジェクトにはモニターとも呼ばれるロックマーク(モニター)があります。複数のスレッドが同時にオブジェクトにアクセスする場合、スレッドはオブジェクトのロックを取得した場合にのみオブジェクトにアクセスできます。
3. Javaでは、synchronizedキーワードを使用してメソッドまたはコードブロックをマークできます。スレッドがオブジェクトの同期メソッドを呼び出すか、同期コードブロックにアクセスすると、このスレッドはオブジェクトのロックを取得し、他のスレッドは一時的にできません。このメソッドにアクセスするには、このメソッドの実行またはコードブロックの実行を待つだけで、このスレッドはオブジェクトのロックを解放し、他のスレッドはこのメソッドまたはコードブロックを実行できます。

 ただし、注意すべき点がいくつかあります。

  1)スレッドがオブジェクトの同期メソッドにアクセスしている場合、他のスレッドはオブジェクトの他の同期メソッドにアクセスできません。オブジェクトにはロックが1つしかないため、この理由は単純です。スレッドがオブジェクトのロックを取得した後、他のスレッドはオブジェクトのロックを取得できないため、オブジェクトの他の同期されたメソッドにアクセスできません。

  2)スレッドがオブジェクトの同期されたメソッドにアクセスしているとき、他のスレッドはオブジェクトの同期されていないメソッドにアクセスできます。この理由は非常に単純です。同期されていないメソッドにアクセスするためにオブジェクトのロックを取得する必要はありません。メソッドがsynchronizedキーワードで変更されていない場合、重要なリソースを使用しないため、他のスレッドが使用されます。このメソッドにアクセスできます。

  3)スレッドAがobject1の同期メソッドfun1にアクセスする必要があり、別のスレッドBがobject2の同期メソッドfun1にアクセスする必要がある場合、object1とobject2が同じタイプであっても、スレッドセーフの問題は発生しません。異なるオブジェクトにアクセスするため、相互排除の問題はありません。

同期メソッドまたは同期コードブロックの場合、例外が発生すると、JVMは現在のスレッドによって占有されているロックを自動的に解放するため、例外によるデッドロックは発生しません。

揮発性

共有変数(クラスメンバー変数、クラス静的メンバー変数)がvolatileによって変更されると、セマンティクスの2つの層があります。

  1)この変数の可視性は、異なるスレッドが動作しているときに保証されます。つまり、スレッドが変数の値を変更した場合、新しい値は他のスレッドにすぐに表示されます。

  2)指示の並べ替えは禁止されています。

同期キーワードは、複数のスレッドが同時にコードを実行することを防ぎ、プログラム実行の効率に大きく影響します。volatileキーワードは、同期よりもパフォーマンスが優れている場合がありますが、volatileキーワードを置き換えることはできないことに注意してください。同期キーワード。、volatileキーワードは、操作の原子性を保証できないため。一般的に、揮発性物質の使用は、次の2つの条件を満たす必要があります。

  1)変数への書き込み操作は現在の値に依存しません

  2)変数は他の変数との不変量に​​含まれていません

  実際、これらの条件は、揮発性変数に書き込むことができる有効な値が、変数の現在の状態を含む、プログラムの状態とは無関係であることを示しています。

ReentrantLock

JDK5.0バージョンより前は、再入可能ロックのパフォーマンスは、synchronizedキーワードよりもはるかに優れていました。JDK6.0バージョン以降、同期は大幅に最適化されており、2つのパフォーマンスは同じではありませんが、再入可能ロックは可能です。キーワードを完全に置き換えます。さらに、リエントラントロックには、一連の強力なUBFF(割り込み可能な応答、ロックアプリケーションの待機時間制限、フェアロック)も付属しています。また、コンディションと組み合わせて使用​​することで、さらに充実させることができます。

  • フェアロック

    フェアロックは毎回同期キューの最初のノードからロックを取得し、フェアロックは同期キューの最初のノードとして毎回ロックを取得し、リソースを要求するときの絶対的な順序を保証します。時間の絶対的な順序を確保するために、フェアロックでは頻繁なコンテキスト切り替えが必要ですが、非フェアロックでは特定のコンテキスト切り替えが減り、パフォーマンスのオーバーヘッドが減ります。したがって、ReentrantLockはデフォルトで不公平なロックを選択して、一部のコンテキストスイッチングを減らし、システムスループットを向上させます。

  • 不当なロック

    不公平なロックは必ずしも必要ではありません。ロックを解除したばかりのスレッドが再びロックを取得する可能性があります。公正なロックにより、ロックを解放したばかりのスレッドが次回ロックを取得し続ける可能性があります。これにより、他のスレッドがロックを取得できなくなり、「飢餓」現象が発生する可能性があります。

ロックまたはその他の関連する同期デバイスの基本的なフレームワークを構築するために使用できるFIFOベースのキューを提供します。シンクロナイザー(以下、シンクロナイザーと呼びます)は、intを使用して状態を表し、ほとんどの同期要件を達成するための基礎になることが期待されます。使用されるメソッドは継承です。サブクラスはシンクロナイザーを継承することによって状態を管理し、そのメソッドを実装する必要があります。管理の方法は、取得と解放と同様のメソッドを介して状態を操作することです。ただし、マルチスレッド環境での状態の操作では原子性を確保する必要があるため、サブクラスはこのシンクロナイザーが提供する次の3つのメソッドを使用して、状態を操作して状態を把握する必要があります。

java.util.concurrent.locks.AbstractQueuedSynchronizer.getState()
java.util.concurrent.locks.AbstractQueuedSynchronizer.setState(int)
java.util.concurrent.locks.AbstractQueuedSynchronizer.compareAndSetState(int, int)

サブクラスは、カスタム同期デバイスの内部クラスとして定義することをお勧めします。シンクロナイザー自体は同期インターフェイスを実装せず、使用する取得メソッドやその他のメソッドを定義するだけです。シンクロナイザーは、排他モードまたは共有モードとして使用できます。排他モードとして定義されている場合、他のスレッドによる取得がブロックされ、複数のスレッドで共有モードが成功する可能性があります。

シンクロナイザーはロックを実現するための鍵であり、シンクロナイザーはロックのセマンティクスを実現するために使用され、シンクロナイザーはロックの実現に集約されます。これは次のように理解できます。ロックAPIはユーザー指向であり、ロックと対話するパブリック動作を定義します。各ロックは、これらの動作を通じて特定の操作を完了する必要があります(たとえば、2つのスレッドに追加を許可できます)。ロック(3つ以上のスレッドを除く)。ただし、実装はシンクロナイザーに依存して行われます。シンクロナイザーは、スレッドアクセスとリソース制御用であり、スレッドがリソースとスレッドキューイングおよびその他の操作を取得できるかどうかを定義します。ロックとシンクロナイザーは、2つが注意を払う必要のある領域を適切に分離します。厳密に言えば、シンクロナイザーは、ロック以外の他の同期機能(ロックを含む)に適用できます。

AbstractQueuedSynchronizerは、キューシンクロナイザー(以下、AQS)とも呼ばれます。ロックやその他の同期コンポーネントを構築するために使用されます。基本的なフレームワークは、int型のメンバー変数stateを使用して同期状態を制御します。state= 0の場合、それはそこにあることを意味します。 is noスレッドは共有リソースのロックを占有します。state= 1の場合、現在共有変数を使用しているスレッドがあり、他のスレッドは同期キューに参加して待機する必要があることを意味します。AQSは内部ノードを使用して内部ノードを形成します。スレッド取得ロックキューを完了するためのFIFO同期キュー同時に、内部クラスConditionObjectを使用して待機キューを構築します。Conditionがwait()メソッドを呼び出すと、スレッドは待機キューに参加し、Conditionが呼び出すとsignal()メソッドの場合、スレッドは待機キューからモバイル同期キューに移動して、競合をロックします。ここには2つのキューが含まれていることに注意してください。1つは同期キューです。スレッドがロックを要求して待機すると、同期キューに参加して待機し、もう1つは待機キュー(複数存在する可能性があります)であり、awaitを呼び出します。 ()条件によるメソッドロックが解除されると、待機キューに追加されます。

PS:理論はたくさんあります。要点をよく見てください。ソースコードで共有されているpdfと公式ウェブサイトのスレッドの紹介は、中国語で中国語に翻訳されているのでわかりやすいです。

おすすめ

転載: blog.51cto.com/12040702/2660400