https://medium.com/@itsromiljain/curious-case-of-concurrenthashmap-90249632d335 この概念的な話は約1.7であります
https://crossoverjie.top/2018/07/23/java-senior/ConcurrentHashMap/ 良い書き込みの分析と比較
JDKを参照するには、特定のコード
1.7
/ * * データを格納する際*セグメント配列は、最初の特定のセグメントを検索する必要があります。 * / 最後のセグメント <K、V> []セグメント。 一過性の設定 <K> のkeySet。 一過性の設定 <のMap.Entry <K、V >>のentrySet。
セグメントは、内部クラスであります
静的最終的なクラスのセグメント<K、V>はReentrantLockの実装シリアライズ{延び プライベート 静的最終長いのserialVersionUID = 2249069246763182397Lを、 // のHashMapおよびHashEntry役割、浴槽内に格納された実データ の過渡揮発性 HashEntry <K、V> [表。 過渡int型のカウント。 一過性のint modCount。 過渡int型のしきい値。 最終フロートloadFactor。 }
HashEnrty
静的最終的なクラス HashEnrty <K、V> { 最終int型のハッシュ。 最後のKキー。 揮発性のV値。 揮発性 HashEnrty <K、V> 次。 }
1.ConcurrentHashMapはとても同時変更の際に、対応するセグメントがロックされ、達成セグメント自体はReentrantLockの拡大に基づいており、データの一貫性を確保するために再入国ロックを取得します。
初期段階2.反復スキャンは、アレイ内の対応するキー値が内部既にあるかどうかを決定してから、操作に入れ、更新、または、あなたがコードに対応するコメントを見ることができるかどうかを決定します。スキャンを繰り返し、競合を検出するのConcurrentHashMapの一般的な技術です。
3.これは、拡張の全体的な行為ではなく、拡張のための別途のセグメント
1.8
使用は一方で彼は、元のセグメントのセグメントロックを放棄した CAS + synchronized
同時実行の安全性を確保します。
最終 V putVal(Kキー、V値、ブールonlyIfAbsent){ 場合(キー== NULL ||値== NULL)スロー 新しい(NullPointerExceptionが)。 int型のハッシュ= スプレッド(key.hashCode()); int型 binCount = 0 ; ため(ノード<K、V> []タブ= 表;;){ ノード <K、V> F; INT N-、I、FH、FK K、VのFv; IF(タブ== NULL ||(= N-tab.length)== 0 )//裁判官初期化するかどうか タブ = initTable(); そう であれば((F = tabAt(タブ、I =(N - 1)・ハッシュ))== NULL ){//进行写数据 場合(casTabAt(タブ、I、ヌル、新しいノード<K、V> (ハッシュ、キー、値))) 休憩。 // 空のビンに追加することなしロックしない } 他の 場合((FH = f.hash)== //移動)さ扩容 タブ = helpTransfer(タブ、F)。 それ以外の 場合(onlyIfAbsent //はロックを取得せずに、最初のノードをチェック && FH == ハッシュ &&((FK = f.key)==キー||(FK!= nullの && key.equals(FK))) &&(FV = F !.val)= nullの) リターンFV。 他の{ OLDVAL V = NULL ; 同期(F){//書き込み、ロックを確保するために IF(tabAt(タブ、I)== F){ IF(FH> = 0 ){ binCount = 1 。 ため(ノード<K、V> E = F ;; ++ binCount){ B; もし(e.hash ==ハッシュ&& ((EK = e.key)==キー|| (EK!= nullの && key.equals(EK)))){ OLDVAL = e.val。 もし(!onlyIfAbsent) e.val = 値。 休憩; } ノード <K、V> PRED = E。 もし((E = e.next)== NULL ){ pred.next = 新しいノード<K、V> (ハッシュ、キー、値)。 休憩; } } } そう であれば(F instanceofのTreeBin){ ノード <K、V> P。 binCount = 2 。 もし((P =((TreeBin <K、V> )F).putTreeVal(ハッシュ、キー、 値))!= nullの){ OLDVAL = p.val。 もし(!onlyIfAbsent) p.val = 値。 } } それ以外の 場合(F instanceofはReservationNode) スロー 新しい IllegalStateExceptionが( "再帰更新" ); } } もし(binCount!= 0 ){ 場合(binCount> = TREEIFY_THRESHOLD) treeifyBin(タブ、I); //変換黒い木 もし(!OLDVAL = nullの) リターンOLDVAL。 休憩; } } } addCount( 1L 、binCount)。 リターン ヌル。 }
1.これは、拡張の全体的な行為ではなく、拡張のための別途のセグメント
2.彼らはもはや使用セグメントので、初期化動作が大幅に効率的に人に文句を言うのたくさんのこの古いバージョンを解決するために、最初のオーバーヘッドを回避することができます怠惰な負荷のフォームに修正、簡略化されています。
前記不揮発性データ・ストレージを使用することは、視認性を確保します。
4. CASおよびその他の操作は、特定のロック同時動作シナリオで行わ。
5.安全でない、底手段等LongAdder、極端な状況を最適化します。
それはむしろ、なぜこれがある、などの通常の推奨ReentrantLockのよりも、同期使用しますか?現代のJDKでは、常に最適化された同期、メモリ消費量を削減ReentrantLockのに比べて、加えて、パフォーマンスの違いについてあまり心配することはできません、これは非常に大きな利点があります。