ホームページ  >  記事  >  Java  >  Java 同時実行性を学ぶ: ロックの最適化、ConcurrentHashMap、ロックの分離

Java 同時実行性を学ぶ: ロックの最適化、ConcurrentHashMap、ロックの分離

php是最好的语言
php是最好的语言オリジナル
2018-07-30 10:02:151846ブラウズ

ロックのパフォーマンスを向上させるためのいくつかの提案


ロック保持時間を短縮する

必要な場合にのみ同期し、ロック保持時間を大幅に短縮し、ロック競合の可能性を減らし同時実行性を向上させます

たとえば、synchronize 同期ロックを使用して、オブジェクトが変数の状態を共有する必要があるときに追加するようにしてください。メソッド全体の前に synchronize を追加するのではなく、このメソッドを呼び出すオブジェクトを直接ロックすると、ロック競合の可能性が高くなります。


読み取り/書き込みロック分離は排他的ロックを置き換えます

効率を向上させるために ReadWriteLock 読み取り/書き込み分離を使用することについて話しました

それはさらにロック分離戦略に拡張されます

排他的ロック分離のテクノロジーへの典型的なリファレンスシーンは LinkedBlockingQueue タスク キューであり、リンクされたリストに基づいて実装された無制限のタスク キューであり、その take() メソッドと put() メソッドはそれぞれキューのフロントエンドとテールエンドで動作します。補完的な効果があるため、jdk では、これら 2 つの操作に対して 2 つのロックが提供されます

たとえば、put() 操作のマルチスレッド実行では、putLock に対して最も多くの競合が必要となり、take 操作は takeLock に対して競合します。リソースのロックを 1 回限りのロック要求に統合できるため、アプリケーションとリリースのアクションの消費が削減されます。


例:

 /** Lock held by take, poll, etc */
    private final ReentrantLock takeLock = new ReentrantLock();

    /** Wait queue for waiting takes */
    private final Condition notEmpty = takeLock.newCondition();

    /** Lock held by put, offer, etc */
    private final ReentrantLock putLock = new ReentrantLock();

    /** Wait queue for waiting puts */
    private final Condition notFull = putLock.newCondition();

は、ロック粒度を減らすために

for(int i=0;i<n;i++){
		synchronized(lock){}
		
	}

に最適化されています

このテクノロジーの典型的なアプリケーション シナリオは、HashMap と比較してスレッドセーフであり、HashTable と比較して、スレッドセーフです。効率的な同時実行


ConcurrentHashMap の実装原理

jdk1.7 と jdk1.8 の ConcurrentHashMap の実装には大きな違いがあります

ConcurrentHashMap の基礎となる構造はセグメント配列であり、デフォルトのサイズは 16 で、各セグメント配列は次のことができます。つまり、セグメント配列はリンクされたリストと配列を使用して実装されます。

jdk 1.7はセグメンテーションロックに基づいてConcurrentHashMapを実装します
  • たとえば、新しいキーと値のペアをマップに挿入する必要がある場合、まずキーのハッシュコードに従ってそれを挿入する必要があるセグメントを見つけてから、このセグメントをロックして挿入を完了しますここでのセグメントはロックとして機能します。

    Segment クラスは ReentrantLock クラス

    を継承しているため、ロックの取得に失敗するとこのメソッドがハングするため、ロックを取得するときに直接ロックを使用することはありません。実際には、
  • スピン ロック
を使用します。tryLock がロックの取得に失敗した場合、ロックは他のスレッドによって占有されていることを意味します。このとき、ループを通じて tryLock を通じてロックが再度適用されます。したがって、マルチスレッドでは、挿入されたデータがセグメント内になく、ロックの競合によってブロッキングが発生しない限り、スレッド間で真の同時実行性を実現できます。

問題: セグメントをまたがる操作が必要な場合、つまりシステムがグローバル ロックを取得する必要がある場合、すべてのセグメントのロックを正常に取得することによってのみ、各セグメントのロック状態を確認する必要があります。グローバルな情報を取得できます。たとえば、ConcurrentHashMap の size() メソッドでは、すべてのサブセグメントのロックを取得する必要があります

Java 同時実行性を学ぶ: ロックの最適化、ConcurrentHashMap、ロックの分離

CAS に基づく ConcurrentHashMap の jdk 1.8 実装
  • jdk1.7 の ConcurrentHashMap の最大同時実行数はセグメントの数と同じです。 同時実行性をさらに向上させるために、jdk 1.8 ではセグメンテーション ロック ソリューションを放棄し、大きな配列を直接使用しました。同時に、ハッシュ衝突時のアドレス指定パフォーマンスを向上させるために、Java 8 は、リンク リスト (アドレス指定時間の複雑さは O(N)) を赤黒ツリー (アドレス指定時間の複雑さは O(N)) に変換します。リンクされたリストの長さが特定のしきい値 (8) O(long(N))) を超えています。配列内のキーのインデックスも、キーのハッシュ値と配列の長さのモジュロを取ることによって決定されます

put 操作の場合、キーに対応する配列要素が null の場合、キーに設定されます。 CAS 操作による現在値。 Key に対応する配列要素 (つまり、リンク リストの先頭またはツリーのルート要素) が null でない場合は、synchronized キーワードを使用して要素にロックを適用し、操作を実行します。 put 操作によって現在のリンク リストの長さが特定のしきい値を超える場合、リンク リストはツリーに変換され、それによってアドレス指定効率が向上します。

読み取り操作の場合、配列は volatile キーワードで変更されるため、配列の可視性について心配する必要はありません。同時に、各要素は Node インスタンスです (Java 7 の各要素は HashEntry です)。その Key 値とハッシュ値は、final によって変更され、変更後の可視性を気にする必要はありません。そのValueと次の要素への参照はvolatileによって変更され、可視性も保証されます。 Java 同時実行性を学ぶ: ロックの最適化、ConcurrentHashMap、ロックの分離

サイズ操作: 各大きな配列はカウンターを維持します。put メソッドと Remove メソッドは両方とも、addCount メソッドを通じて Map のサイズを維持します。 size メソッドは、addCount メソッドによって保持される Map のサイズを sumCount を通じて取得します。

ConcurrentHashMap でグローバル カウンターを使用する代わりに各配列にカウンターが含まれる理由は、ConcurrentHashMap の同時実行性を考慮するためであるという事実に特別な注意を払う必要があります。 このように、カウンターを更新する必要がある場合、 ConcurrentHashMap 全体をロックする必要はありません count は揮発性であることに注意することが重要です。そのため、count の更新は他のスレッドにすぐに表示されます

関連記事:

Java フレームワーク Bootstrap、jQuery、SpringMVC、Hibernate高いパフォーマンスの同時実行性

Java システムにおける高い同時実行性の問題の解決策

関連ビデオ:

Java ビデオ チュートリアル

以上がJava 同時実行性を学ぶ: ロックの最適化、ConcurrentHashMap、ロックの分離の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。