concurrenthashmap code reading
TRANSCRIPT
![Page 2: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/2.jpg)
Agenda
•準備•Overview•読む•まとめ
![Page 3: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/3.jpg)
準備
![Page 4: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/4.jpg)
準備するもの
• Eclipse• ソース• ConcurrentHashMap.java• テストケース• JSR166TestCase.java• ConcurrentHashMapTest.java
• ConcurrentHashMapに関する参照資料• 気合い
![Page 5: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/5.jpg)
ソースの取得場所
•ConcurrentHashMap.javaはJDKより•テストケースはDoug Leaさんのサイトよりhttp://gee.cs.oswego.edu/dl/concurrency-interest/
![Page 6: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/6.jpg)
参照した資料
• JavaDochttp://java.sun.com/javase/ja/6/docs/ja/api/java/util/concurrent/ConcurrentHashMap.html
• IBM developerWorks:優れたHashMapの構築http://www.ibm.com/developerworks/jp/java/library/j-jtp08223/
• ITアーキテクト J2SE 5.0の新機能http://www.itarchitect.jp/technology_and_programming/-/24161-3.html
• Servlet Gardern@はてなhttp://d.hatena.ne.jp/yokolet/20071005
![Page 7: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/7.jpg)
Overview
![Page 8: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/8.jpg)
Overview
• Constants: 6つ• Field: 6つ• ユーティリティ: 2つ(hash(), segmentFor())• インナークラス: 2つ(HashEntry, Segment)• Public メソッド: 24コ(Constructorは5つ)• Iteratorサポート: 8つ• Serializationサポート: 2つ(writeObject(), readObject())
![Page 9: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/9.jpg)
多分重要なトコ
• Constants: 6つ• Field: 6つ• ユーティリティ: 2つ(hash(), segmentFor())• インナークラス: 2つ(HashEntry, Segment)• Public メソッド: 24コ(Constructorは5つ)• Iteratorサポート: 8つ• Serializationサポート: 2つ(writeObject(), readObject())
![Page 10: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/10.jpg)
今回は割愛するトコ
• Constants: 6つ• Field: 6つ• ユーティリティ: 2つ(hash(), segmentFor())• インナークラス: 2つ(HashEntry, Segment)• Public メソッド: 24コ(Constructorは5つ)• Iteratorサポート: 8つ• Serializationサポート: 2つ(writeObject(), readObject())
![Page 11: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/11.jpg)
読む
![Page 12: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/12.jpg)
public ConcurrentHashMap() { this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);}
public ConcurrentHashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR, DEFAULT_CONCURRENCY_LEVEL);}
public ConcurrentHashMap(int initialCapacity, float loadFactor) { this(initialCapacity, loadFactor, DEFAULT_CONCURRENCY_LEVEL);}
コンストラクタ
![Page 13: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/13.jpg)
/** デフォルトの初期容量 */
static final int DEFAULT_INITIAL_CAPACITY = 16;/** デフォルトの負荷係数 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;/** デフォルトの並行処理レベル */
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
コンストラクタ
![Page 14: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/14.jpg)
/** 最大Segment数 1を16ビット分左にシフトしてるので65536 */
static final int MAX_SEGMENTS = 1 << 16;. . .public ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel) { if (!(loadFactor > 0) || initialCapacity < 0 || concurrencyLevel <= 0) throw new IllegalArgumentException();
// 並行処理レベルが最大セグメント数を超えてはならない if (concurrencyLevel > MAX_SEGMENTS) concurrencyLevel = MAX_SEGMENTS;
. . .
コンストラクタ
![Page 15: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/15.jpg)
// SegmentとHashエントリの最適な引数を見つける int sshift = 0; int ssize = 1; while (ssize < concurrencyLevel) { ++sshift; ssize <<= 1; } // currencyLevelが16(デフォルト)の時、sshiftは4、ssizeは16になる segmentShift = 32 - sshift; // 28(32は32bitのこと?)
segmentMask = ssize - 1; // 15(0b0111) this.segments = Segment.newArray(ssize);
Segment数の決定/** どのSegmentにあるかを特定するためのマスク値 */
final int segmentMask;/** どのSegmentにあるかを特定するためのシフト値 */
final int segmentShift;
![Page 16: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/16.jpg)
// Segmentの初期容量の算出 if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; int c = initialCapacity / ssize; if (c * ssize < initialCapacity) ++c; int cap = 1; while (cap < c) cap <<= 1;
for (int i = 0; i < this.segments.length; ++i) this.segments[i] = new Segment<K,V>(cap, loadFactor);}
Segmentの生成/** 最大容量 1を30ビット分左にシフトしてるので1073741824 */
static final int MAXIMUM_CAPACITY = 1 << 30;
![Page 17: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/17.jpg)
Segmentとは
•ConcurrentHashMap.javaにて定義されているインナークラス
• 1つのConcurrentHashMapインスタンスに複数(デフォルト16)のSegmentを持つ
•ハッシュテーブルの特別バージョン• ReentrantLockのサブクラス
![Page 18: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/18.jpg)
static final class Segment<K,V> extends ReentrantLock implements Serializable {. . .transient int threshold;// 各SegmentにHashEntryの配列を保持transient volatile HashEntry<K,V>[] table;. . .Segment(int initialCapacity, float lf) { loadFactor = lf; setTable(HashEntry.<K,V>newArray(initialCapacity));}. . .void setTable(HashEntry<K,V>[] newTable) { threshold = (int)(newTable.length * loadFactor); table = newTable;}
Segmentのコンストラクタ
![Page 19: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/19.jpg)
HashEntryとは
•ConcurrentHashMap.javaにて定義されているインナークラス
• 1つのSegmentインスタンスに1つ以上のHashEntryを持つ
•フィールドにkey, hash値, value, next(次のHashEntry)を持つ
• key, hash, nextはfinal
![Page 20: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/20.jpg)
static final class HashEntry<K,V> { final K key; final int hash; volatile V value; final HashEntry<K,V> next; HashEntry(K key, int hash, HashEntry<K,V> next, V value) { this.key = key; this.hash = hash; this.next = next; this.value = value; } @SuppressWarnings("unchecked") static final <K,V> HashEntry<K,V>[] newArray(int i) { return new HashEntry[i]; }}
HashEntry
![Page 21: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/21.jpg)
Segment、HashEntryの構造
![Page 22: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/22.jpg)
ConcurrentHashMap
Segment、HashEntryの構造
![Page 23: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/23.jpg)
ConcurrentHashMap
Segment
Segment
Segment
・・・
Segment、HashEntryの構造
![Page 24: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/24.jpg)
ConcurrentHashMap
Segment
Segment
Segment
HashEntry
・・・
Segment、HashEntryの構造
![Page 25: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/25.jpg)
ConcurrentHashMap
Segment
Segment
Segment
HashEntry
・・・
HashEntry
Segment、HashEntryの構造
![Page 26: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/26.jpg)
ConcurrentHashMap
Segment
Segment
Segment
HashEntry
・・
HashEntry
・
HashEntry
Segment、HashEntryの構造
![Page 27: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/27.jpg)
ConcurrentHashMap
Segment
Segment
Segment
HashEntry
・・
HashEntry
・
HashEntry
HashEntry
Segment、HashEntryの構造
![Page 28: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/28.jpg)
ConcurrentHashMap
Segment
Segment
Segment
HashEntry
・・
HashEntry
・
HashEntry
HashEntry
HashEntry
Segment、HashEntryの構造
![Page 29: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/29.jpg)
ConcurrentHashMap
Segment
Segment
Segment
HashEntry
・・
HashEntry
・
HashEntry
HashEntry
HashEntry
Segment、HashEntryの構造
実際のチェーン数は1~2個くらい
![Page 30: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/30.jpg)
ConcurrentHashMap
Segment
Segment
Segment
HashEntry
・・
HashEntry
・
HashEntry
HashEntry
HashEntry
HashEntry
Segment、HashEntryの構造
実際のチェーン数は1~2個くらい
![Page 31: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/31.jpg)
ConcurrentHashMap
Segment
Segment
Segment
HashEntry
・・
HashEntry
・
HashEntry
HashEntry
HashEntry
HashEntry
Segment、HashEntryの構造
実際のチェーン数は1~2個くらい
全Segmentが埋まったらSegmentのサイズは2倍に拡張される
![Page 32: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/32.jpg)
public V get(Object key) {// 与えられたハッシュ値を元にちゃんとしたハッシュ値を求める
int hash = hash(key.hashCode());// ハッシュ値に該当するセグメントを見つけSegment#getをcall
return segmentFor(hash).get(key, hash);}
ConcurrentHashMap#get
![Page 33: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/33.jpg)
public boolean containsKey(Object key) { int hash = hash(key.hashCode()); return segmentFor(hash).containsKey(key, hash);}public V put(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, false);}public V putIfAbsent(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, true);}
他の主要メソッドも似たような処理
public boolean remove(Object key, Object value) { int hash = hash(key.hashCode()); if (value == null) return false; return segmentFor(hash).remove(key, hash, value) != null;}public boolean replace(K key, V oldValue, V newValue) { if (oldValue == null || newValue == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, oldValue, newValue);}public V replace(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, value);}
![Page 34: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/34.jpg)
public boolean containsKey(Object key) { int hash = hash(key.hashCode()); return segmentFor(hash).containsKey(key, hash);}public V put(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, false);}public V putIfAbsent(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).put(key, hash, value, true);}
他の主要メソッドも似たような処理
public boolean remove(Object key, Object value) { int hash = hash(key.hashCode()); if (value == null) return false; return segmentFor(hash).remove(key, hash, value) != null;}public boolean replace(K key, V oldValue, V newValue) { if (oldValue == null || newValue == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, oldValue, newValue);}public V replace(K key, V value) { if (value == null) throw new NullPointerException(); int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, value);}
![Page 35: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/35.jpg)
private static int hash(int h) { // Spread bits to regularize both segment and index locations, // using variant of single-word Wang/Jenkins hash. h += (h << 15) ^ 0xffffcd7d; h ^= (h >>> 10); h += (h << 3); h ^= (h >>> 6); h += (h << 2) + (h << 14); return h ^ (h >>> 16);}
ConcurrentHashMap#hash
何やっているかわかりません・・・orz
http://www.concentric.net/~Ttwang/tech/inthash.htm が参考ソース?
HashMap#hashよりBetterな実装に置き換えてる
![Page 36: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/36.jpg)
ConcurrentHashMap
Segment
Segment
Segment
・・・
いいハッシュ値を返すと・・・
![Page 37: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/37.jpg)
ConcurrentHashMap
Segment
Segment
Segment
・・・
いいハッシュ値を返すと・・・
PUT
(“1”, “a”)
![Page 38: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/38.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
いいハッシュ値を返すと・・・
![Page 39: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/39.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
いいハッシュ値を返すと・・・
(“2”, “b”)
PUT
![Page 40: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/40.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
(“2”, “b”)
いいハッシュ値を返すと・・・
![Page 41: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/41.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
(“2”, “b”)
いいハッシュ値を返すと・・・
(“3”, “c”)
PUT
![Page 42: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/42.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
(“2”, “b”)
(“3”, “c”)
いいハッシュ値を返すと・・・
![Page 43: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/43.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
(“2”, “b”)
(“3”, “c”)
いいハッシュ値を返すと・・・
ちゃんとバラけてくれる!
![Page 44: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/44.jpg)
ConcurrentHashMap
Segment
Segment
Segment
・・・
悪いハッシュ値を返すと・・・
![Page 45: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/45.jpg)
ConcurrentHashMap
Segment
Segment
Segment
・・・
悪いハッシュ値を返すと・・・
PUT
(“1”, “a”)
![Page 46: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/46.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
悪いハッシュ値を返すと・・・
![Page 47: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/47.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
悪いハッシュ値を返すと・・・
(“2”, “b”)
PUT
![Page 48: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/48.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
(“2”, “b”)
悪いハッシュ値を返すと・・・
![Page 49: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/49.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
(“2”, “b”)
悪いハッシュ値を返すと・・・
(“3”, “c”)
PUT
![Page 50: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/50.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
(“2”, “b”) (“3”, “c”)
悪いハッシュ値を返すと・・・
![Page 51: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/51.jpg)
ConcurrentHashMap
Segment
Segment
Segment
(“1”, “a”)
・・・
(“2”, “b”) (“3”, “c”)
悪いハッシュ値を返すと・・・
偏ってしまい、すごく効率が悪くなる!
![Page 52: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/52.jpg)
V get(Object key, int hash) { if (count != 0) { // read-volatile
// hashに相当するHashEntryの要素を取り出す HashEntry<K,V> e = getFirst(hash); while (e != null) { if (e.hash == hash && key.equals(e.key)) {
// HashEntryのハッシュ値とキーが両方とも合えば V v = e.value; if (v != null) return v; return readValueUnderLock(e); // recheck } e = e.next; } } return null;}
Segment#get
![Page 53: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/53.jpg)
boolean containsKey(Object key, int hash) { if (count != 0) { // read-volatile HashEntry<K,V> e = getFirst(hash); while (e != null) { if (e.hash == hash && key.equals(e.key)) return true; e = e.next; } } return false;}
Segment#containsKey基本Segment#getと同じ
![Page 54: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/54.jpg)
boolean containsValue(Object value) { if (count != 0) { // read-volatile HashEntry<K,V>[] tab = table; // 変化する恐れがあるので作業用に置いてる? int len = tab.length; for (int i = 0 ; i < len; i++) { for (HashEntry<K,V> e = tab[i]; e != null; e = e.next) { V v = e.value; if (v == null) // recheck v = readValueUnderLock(e); // 理屈上こういうことがありうるらしい if (value.equals(v)) return true; } } } return false;}
Segment#containsValue
![Page 55: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/55.jpg)
V put(K key, int hash, V value, boolean onlyIfAbsent) { lock(); // HashEntryの更新系(put, remove, replace...)はロックが必要 try { int c = count; if (c++ > threshold) // ensure capacity rehash(); // サイズがしきい値を超えたら各要素のハッシュ値を再計算 HashEntry<K,V>[] tab = table; int index = hash & (tab.length - 1); HashEntry<K,V> first = tab[index]; HashEntry<K,V> e = first; while (e != null && (e.hash != hash || !key.equals(e.key))) // どういうケース?
e = e.next;. . .
Segment#put
![Page 56: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/56.jpg)
V oldValue; if (e != null) { oldValue = e.value; if (! onlyIfAbsent) e.value = value; } else { // 基本はここを通る oldValue = null; ++modCount; // HashEntryの構造が変更された回数を増やす tab[index] = new HashEntry<K,V>(key, hash, first, value); count = c; // write-volatile } return oldValue; } finally { unlock(); }}
Segment#put
![Page 57: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/57.jpg)
V remove(Object key, int hash, Object value) { lock(); try { int c = count - 1; // サイズを1つ減らす HashEntry<K,V>[] tab = table; int index = hash & (tab.length - 1); HashEntry<K,V> first = tab[index]; HashEntry<K,V> e = first; while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next; V oldValue = null;. . .
Segment#remove前半部分はSegment#putと大体同じ
![Page 58: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/58.jpg)
if (e != null) { V v = e.value; if (value == null || value.equals(v)) { oldValue = v; ++modCount; HashEntry<K,V> newFirst = e.next; for (HashEntry<K,V> p = first; p != e; p = p.next) newFirst = new HashEntry<K,V>(p.key, p.hash, newFirst, p.value); tab[index] = newFirst; count = c; // write-volatile } } return oldValue; } finally { unlock(); }}
Segment#remove
![Page 59: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/59.jpg)
if (e != null) { V v = e.value; if (value == null || value.equals(v)) { oldValue = v; ++modCount; HashEntry<K,V> newFirst = e.next; for (HashEntry<K,V> p = first; p != e; p = p.next) newFirst = new HashEntry<K,V>(p.key, p.hash, newFirst, p.value); tab[index] = newFirst; count = c; // write-volatile } } return oldValue; } finally { unlock(); }}
Segment#remove
![Page 60: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/60.jpg)
Segment#remove
• HashEntryにはnextという次の要素を表すフィールドがある
• removeが行われるとnextの付け替えが発生する
• nextフィールドはfinalなので削除した要素より前を作り直さなければならない
![Page 61: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/61.jpg)
SegmentA
Next = B
BNext = C
CNext = D
DNext = E
ENext = null
![Page 62: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/62.jpg)
SegmentA
Next = B
BNext = C
CNext = D
DNext = E
ENext = null
remove(“C”)
![Page 63: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/63.jpg)
SegmentA
Next = B
BNext = C
DNext = E
ENext = null
![Page 64: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/64.jpg)
SegmentA
Next = B
BNext = C
DNext = E
ENext = null
NextがfinalなのでDに変更できない!
![Page 65: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/65.jpg)
SegmentA
Next = B
BNext = C
DNext = E
ENext = null
NextがfinalなのでDに変更できない!
AとBは作り直し
![Page 66: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/66.jpg)
void rehash() { HashEntry<K,V>[] oldTable = table; int oldCapacity = oldTable.length; if (oldCapacity >= MAXIMUM_CAPACITY) return;
/* * Reclassify nodes in each list to new Map. Because we are * using power-of-two expansion, the elements from each bin * must either stay at same index, or move with a power of two * offset. We eliminate unnecessary node creation by catching. . .
Segment#rehashput時にサイズがcapacityを超えた時に発生
![Page 67: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/67.jpg)
コメントの超意訳
• 各リストのノードを新しいMapに再分類する。2つの強力な拡張(SegmentとHashEntry)を使っているため、同じindexを保つか2つのoffsetを同時に移動させなければならない。nextフィールドは変わらないので、古いノードは再利用でき不必要なノードを作らなくてすむ。統計的に、デフォルトのthresholdだとテーブルを2倍にする時にクローンする必要があるのは約1/6。置き換わる古いノードはreaderスレッドがすぐにテーブルを走査することで参照されなくなりGC対象となる。
![Page 68: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/68.jpg)
void clear() { if (count != 0) { lock(); try { HashEntry<K,V>[] tab = table; for (int i = 0; i < tab.length ; i++) tab[i] = null; ++modCount; count = 0; // write-volatile } finally { unlock(); } }}
Segment#clear
![Page 69: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/69.jpg)
V replace(K key, int hash, V newValue) { lock(); try { HashEntry<K,V> e = getFirst(hash); while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next;
V oldValue = null; if (e != null) { oldValue = e.value; e.value = newValue; } return oldValue; } finally { unlock(); }}
Segment#replace
![Page 70: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/70.jpg)
boolean replace(K key, int hash, V oldValue, V newValue) { lock(); try { HashEntry<K,V> e = getFirst(hash); while (e != null && (e.hash != hash || !key.equals(e.key))) e = e.next;
boolean replaced = false; if (e != null && oldValue.equals(e.value)) { replaced = true; e.value = newValue; } return replaced; } finally { unlock(); }}
Segment#replace その2
![Page 71: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/71.jpg)
public boolean isEmpty() { final Segment<K,V>[] segments = this.segments; int[] mc = new int[segments.length]; int mcsum = 0; for (int i = 0; i < segments.length; ++i) { if (segments[i].count != 0) return false; else // 全segmentのサイズが0の時、それぞれの変更回数の和を計算する mcsum += mc[i] = segments[i].modCount; }. . .
ConcurrentHashMap#isEmpty
![Page 72: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/72.jpg)
if (mcsum != 0) { // 何かしらの変更 (ABA問題) があった場合 for (int i = 0; i < segments.length; ++i) { if (segments[i].count != 0 || mc[i] != segments[i].modCount) return false; } } return true;}
ConcurrentHashMap#isEmpty
![Page 73: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/73.jpg)
isEmptyのコメント超意訳
• いつの時点でもテーブルが空にならなかった場合、あるsegmentの一要素が追加され別で走査中に削除されるというABA問題(「Java並行処理プログラミング」15章参照)を避けるために各segmentのmodCountを追跡する。他にABA問題の影響を受ける可能性があるsize()やcontainsValue()メソッドでも同様にmodCountを使っている。
![Page 74: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/74.jpg)
ConcurrentHashMap#size
•まずはロック無しで2回までトライする•数えている間に、別で変更処理が行われたらやり直す
• 2回とも邪魔されてしまったら全Segmentにロックをかけて数える
![Page 75: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/75.jpg)
public int size() { final Segment<K,V>[] segments = this.segments; long sum = 0; long check = 0; int[] mc = new int[segments.length]; // Try a few times to get accurate count. On failure due to // continuous async changes in table, resort to locking. for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { check = 0; sum = 0; int mcsum = 0;. . .
ConcurrentHashMap#size/** size()とcontainsValue()で使われる */
static final int RETRIES_BEFORE_LOCK = 2;
![Page 76: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/76.jpg)
for (int i = 0; i < segments.length; ++i) { sum += segments[i].count; mcsum += mc[i] = segments[i].modCount; } if (mcsum != 0) { // 初期状態から変更が行われた for (int i = 0; i < segments.length; ++i) { check += segments[i].count; if (mc[i] != segments[i].modCount) { // 変更された check = -1; // force retry(「Java並行処理プログラミング」P269)
break; } } } if (check == sum) break; }
ConcurrentHashMap#size
![Page 77: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/77.jpg)
if (check != sum) { // Resort to locking all segments sum = 0; for (int i = 0; i < segments.length; ++i) //全Segmentをロック segments[i].lock(); for (int i = 0; i < segments.length; ++i) sum += segments[i].count; for (int i = 0; i < segments.length; ++i) segments[i].unlock(); } if (sum > Integer.MAX_VALUE) return Integer.MAX_VALUE; else return (int)sum;}
ConcurrentHashMap#size
![Page 78: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/78.jpg)
public boolean containsValue(Object value) { if (value == null) throw new NullPointerException(); // See explanation of modCount use above final Segment<K,V>[] segments = this.segments; int[] mc = new int[segments.length];
// Try a few times without locking for (int k = 0; k < RETRIES_BEFORE_LOCK; ++k) { int sum = 0; int mcsum = 0;. . .
ConcurrentHashMap#containsValue
ConcurrentHashMap#sizeと大体同じ
![Page 79: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/79.jpg)
for (int i = 0; i < segments.length; ++i) { int c = segments[i].count; // メモリを同期化 mcsum += mc[i] = segments[i].modCount; if (segments[i].containsValue(value)) return true; } boolean cleanSweep = true; if (mcsum != 0) { // 初期状態から変更された for (int i = 0; i < segments.length; ++i) { int c = segments[i].count; // メモリを同期化 if (mc[i] != segments[i].modCount) { // 変更されたのでやり直し cleanSweep = false; break; } } } if (cleanSweep) return false; }
ConcurrentHashMap#containsValue
![Page 80: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/80.jpg)
// Resort to locking all segments for (int i = 0; i < segments.length; ++i) segments[i].lock(); boolean found = false; try { for (int i = 0; i < segments.length; ++i) { if (segments[i].containsValue(value)) { // Segment#containsValue found = true; break; } } } finally { for (int i = 0; i < segments.length; ++i) segments[i].unlock(); } return found;}
ConcurrentHashMap#containsValue
![Page 81: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/81.jpg)
まとめ
![Page 82: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/82.jpg)
まとめ
•ConcurrentHashMapは、SegmentとHashEntryというデータ構造が肝
•高速化のために色んなことをやってる• finalでread時はロックしないように• hash値計算でちゃんとばらけさせる•ロック処理を最小限に抑える
![Page 83: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/83.jpg)
ちなみに
!"#$$$$%&''!$()*+$,-./01.2$3456$
77768)*+.-./01.6591
: & ! ; < = > ?'
<
:'
:<
&'
&<
!'
@AB08C.
DE9F.G.05
HIE""
JKDE""
HIE><
JKDE><
: & ! ; < = > ?'
<
:'
:<
&'
&<
!'
@AB08C.
DE9F.G.05
HI
JKD
:L$@8M+0 :D$@8M+0
(DN$&6;OK)$P$&$QA/R$5F*.
• ConcurrentHashMapより速いNonBlockingHashMapというのがある
•Azul SystemsのCliff Click氏作
![Page 84: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/84.jpg)
詳しく知りたい人は
• JavaOne資料「A Lock-Free Hash Table」http://www.azulsystems.com/events/javaone_2007/2007_LockFreeHash.pdf
• Blog記事「A Non-Blocking HashTable」http://blogs.azulsystems.com/cliff/2007/03/a_nonblocking_h.html
• Highly Scalable Javahttp://sourceforge.net/projects/high-scale-lib
![Page 85: ConcurrentHashMap Code Reading](https://reader033.vdocuments.mx/reader033/viewer/2022052906/558caae1d8b42a34388b4742/html5/thumbnails/85.jpg)
ご清聴ありがとうございました!