ConcurrentHashMap Code Reading

2,692 views

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,692
On SlideShare
0
From Embeds
0
Number of Embeds
47
Actions
Shares
0
Downloads
13
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

ConcurrentHashMap Code Reading

  1. 1. ConcurrentHashMap Code Reading 角田 直行 <kakuda@gmail.com>
  2. 2. Agenda• 準備• Overview• 読む• まとめ
  3. 3. 準備
  4. 4. 準備するもの• Eclipse• ソース • ConcurrentHashMap.java • テストケース • JSR166TestCase.java • ConcurrentHashMapTest.java• ConcurrentHashMapに関する参照資料• 気合い
  5. 5. ソースの取得場所• ConcurrentHashMap.javaはJDKより• テストケースはDoug Leaさんのサイトより http://gee.cs.oswego.edu/dl/concurrency-interest/
  6. 6. 参照した資料• JavaDoc http://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
  7. 7. Overview
  8. 8. Overview• Constants: 6つ• Field: 6つ• ユーティリティ: 2つ(hash(), segmentFor())• インナークラス: 2つ(HashEntry, Segment)• Public メソッド: 24コ(Constructorは5つ)• Iteratorサポート: 8つ• Serializationサポート: 2つ(writeObject(), readObject())
  9. 9. 多分重要なトコ• Constants: 6つ• Field: 6つ• ユーティリティ: 2つ(hash(), segmentFor())• インナークラス: 2つ(HashEntry, Segment)• Public メソッド: 24コ(Constructorは5つ)• Iteratorサポート: 8つ• Serializationサポート: 2つ(writeObject(), readObject())
  10. 10. 今回は割愛するトコ• Constants: 6つ• Field: 6つ• ユーティリティ: 2つ(hash(), segmentFor())• インナークラス: 2つ(HashEntry, Segment)• Public メソッド: 24コ(Constructorは5つ)• Iteratorサポート: 8つ• Serializationサポート: 2つ(writeObject(), readObject())
  11. 11. 読む
  12. 12. コンストラクタ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);}
  13. 13. コンストラクタ/** デフォルトの初期容量 */static final int DEFAULT_INITIAL_CAPACITY = 16;/** デフォルトの負荷係数 */static final float DEFAULT_LOAD_FACTOR = 0.75f;/** デフォルトの並行処理レベル */static final int DEFAULT_CONCURRENCY_LEVEL = 16;
  14. 14. コンストラクタ/** 最大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; ...
  15. 15. Segment数の決定/** どのSegmentにあるかを特定するためのマスク値 */final int segmentMask;/** どのSegmentにあるかを特定するためのシフト値 */final int segmentShift; // 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);
  16. 16. Segmentの生成/** 最大容量 1を30ビット分左にシフトしてるので1073741824 */static final int MAXIMUM_CAPACITY = 1 << 30; // 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);}
  17. 17. Segmentとは• ConcurrentHashMap.javaにて定義されて いるインナークラス • 1つのConcurrentHashMapインスタンス に複数(デフォルト16)のSegmentを持つ• ハッシュテーブルの特別バージョン• ReentrantLockのサブクラス
  18. 18. Segmentのコンストラクタ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; }
  19. 19. HashEntryとは• ConcurrentHashMap.javaにて定義されて いるインナークラス • 1つのSegmentインスタンスに1つ以上の HashEntryを持つ• フィールドにkey, hash値, value, next(次の HashEntry)を持つ • key, hash, nextはfinal
  20. 20. HashEntrystatic 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]; }}
  21. 21. Segment、HashEntryの構造
  22. 22. Segment、HashEntryの構造 ConcurrentHashMap
  23. 23. Segment、HashEntryの構造 Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  24. 24. Segment、HashEntryの構造 Hash Entry Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  25. 25. Segment、HashEntryの構造 Hash Entry Segment Hash Entry Segment ConcurrentHashMap ・ ・ ・ Segment
  26. 26. Segment、HashEntryの構造 Hash Hash Entry Segment Entry Hash Entry Segment ConcurrentHashMap ・ ・ ・ Segment
  27. 27. Segment、HashEntryの構造 Hash Hash Entry Segment Entry Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ ・ Segment
  28. 28. Segment、HashEntryの構造 Hash Hash Hash Entry Segment Entry Entry Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ ・ Segment
  29. 29. Segment、HashEntryの構造 Hash Hash Hash Entry Segment Entry Entry 実際のチェーン数は 1∼2個くらい Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ ・ Segment
  30. 30. Segment、HashEntryの構造 Hash Hash Hash Entry Segment Entry Entry 実際のチェーン数は 1∼2個くらい Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ ・ Hash Entry Segment
  31. 31. Segment、HashEntryの構造 Hash Hash Hash Entry Segment Entry Entry 実際のチェーン数は 1∼2個くらい Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ 全Segmentが埋まったらSegment ・ のサイズは2倍に拡張される Hash Entry Segment
  32. 32. ConcurrentHashMap#getpublic V get(Object key) { // 与えられたハッシュ値を元にちゃんとしたハッシュ値を求める int hash = hash(key.hashCode()); // ハッシュ値に該当するセグメントを見つけSegment#getをcall return segmentFor(hash).get(key, hash);}
  33. 33. 他の主要メソッドも 似たような処理 public boolean remove(Object key, Object value) { int hash = hash(key.hashCode());public boolean containsKey(Object key) { if (value == null) int hash = hash(key.hashCode()); return false; return segmentFor(hash).containsKey(key, hash); return segmentFor(hash).remove(key, hash, value) !=} null;public V put(K key, V value) { } if (value == null) public boolean replace(K key, V oldValue, V newValue) { throw new NullPointerException(); if (oldValue == null || newValue == null) int hash = hash(key.hashCode()); throw new NullPointerException(); return segmentFor(hash).put(key, hash, value, false); int hash = hash(key.hashCode());} return segmentFor(hash).replace(key, hash, oldValue,public V putIfAbsent(K key, V value) { newValue); if (value == null) } throw new NullPointerException(); public V replace(K key, V value) { int hash = hash(key.hashCode()); if (value == null) return segmentFor(hash).put(key, hash, value, true); throw new NullPointerException();} int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, value); }
  34. 34. 他の主要メソッドも 似たような処理 public boolean remove(Object key, Object value) { int hash = hash(key.hashCode());public boolean containsKey(Object key) { if (value == null) int hash = hash(key.hashCode()); return false; return segmentFor(hash).containsKey(key, hash); return segmentFor(hash).remove(key, hash, value) !=} null;public V put(K key, V value) { } if (value == null) public boolean replace(K key, V oldValue, V newValue) { throw new NullPointerException(); if (oldValue == null || newValue == null) int hash = hash(key.hashCode()); throw new NullPointerException(); return segmentFor(hash).put(key, hash, value, false); int hash = hash(key.hashCode());} return segmentFor(hash).replace(key, hash, oldValue,public V putIfAbsent(K key, V value) { newValue); if (value == null) } throw new NullPointerException(); public V replace(K key, V value) { int hash = hash(key.hashCode()); if (value == null) return segmentFor(hash).put(key, hash, value, true); throw new NullPointerException();} int hash = hash(key.hashCode()); return segmentFor(hash).replace(key, hash, value); }
  35. 35. ConcurrentHashMap#hash 何やっているかわかりません・・・orzprivate 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);} HashMap#hashよりBetterな実装に置き換えてる http://www.concentric.net/ Ttwang/tech/inthash.htm が参考ソース?
  36. 36. いいハッシュ値を返すと・・・ Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  37. 37. いいハッシュ値を返すと・・・ Segment(“1”, “a”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  38. 38. いいハッシュ値を返すと・・・ (“1”, “a”) Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  39. 39. いいハッシュ値を返すと・・・ (“1”, “a”) Segment(“2”, “b”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  40. 40. いいハッシュ値を返すと・・・ (“1”, “a”) Segment (“2”, “b”) Segment ConcurrentHashMap ・ ・ ・ Segment
  41. 41. いいハッシュ値を返すと・・・ (“1”, “a”) Segment(“3”, “c”) (“2”, “b”) SegmentPUT ConcurrentHashMap ・ ・ ・ Segment
  42. 42. いいハッシュ値を返すと・・・ (“1”, “a”) Segment (“2”, “b”) Segment ConcurrentHashMap ・ ・ ・ (“3”, “c”) Segment
  43. 43. いいハッシュ値を返すと・・・ (“1”, “a”) Segment (“2”, “b”) Segmentちゃんとバラけてくれる! ConcurrentHashMap ・ ・ ・ (“3”, “c”) Segment
  44. 44. 悪いハッシュ値を返すと・・・ Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  45. 45. 悪いハッシュ値を返すと・・・ Segment(“1”, “a”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  46. 46. 悪いハッシュ値を返すと・・・ (“1”, “a”) Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  47. 47. 悪いハッシュ値を返すと・・・ (“1”, “a”) Segment(“2”, “b”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  48. 48. 悪いハッシュ値を返すと・・・ (“1”, “a”) (“2”, Segment “b”) Segment ConcurrentHashMap ・ ・ ・ Segment
  49. 49. 悪いハッシュ値を返すと・・・ (“1”, “a”) (“2”, Segment “b”)(“3”, “c”) SegmentPUT ConcurrentHashMap ・ ・ ・ Segment
  50. 50. 悪いハッシュ値を返すと・・・ (“1”, “a”) (“2”, Segment “b”) (“3”, “c”) Segment ConcurrentHashMap ・ ・ ・ Segment
  51. 51. 悪いハッシュ値を返すと・・・ (“1”, “a”) (“2”, Segment “b”) (“3”, “c”) Segment 偏ってしまい、 ConcurrentHashMap ・すごく効率が悪くなる! ・ ・ Segment
  52. 52. Segment#getV 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;}
  53. 53. Segment#containsKey 基本Segment#getと同じ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;}
  54. 54. Segment#containsValueboolean 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;}
  55. 55. Segment#putV 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;...
  56. 56. Segment#put 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(); }}
  57. 57. Segment#remove 前半部分はSegment#putと大体同じ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;...
  58. 58. Segment#remove 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(); }}
  59. 59. Segment#remove 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(); }}
  60. 60. Segment#remove• HashEntryにはnextという次の要素を表す フィールドがある• removeが行われるとnextの付け替えが発生 する• nextフィールドはfinalなので削除した要素よ り前を作り直さなければならない
  61. 61. A B C D E SegmentNext = B Next = C Next = D Next = E Next = null
  62. 62. A B C D E SegmentNext = B Next = C Next = D Next = E Next = null remove(“C”)
  63. 63. A B D E SegmentNext = B Next = C Next = E Next = null
  64. 64. Nextがfinalなので A B Dに変更できない!D E SegmentNext = B Next = C Next = E Next = null
  65. 65. Nextがfinalなので A B Dに変更できない!D E SegmentNext = B Next = C Next = E Next = null AとBは作り直し
  66. 66. Segment#rehash put時にサイズがcapacityを超えた時に発生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...
  67. 67. コメントの超意訳• 各リストのノードを新しいMapに再分類する。2つの強力 な拡張(SegmentとHashEntry)を使っているため、同じ indexを保つか2つのoffsetを同時に移動させなければなら ない。nextフィールドは変わらないので、古いノードは再 利用でき不必要なノードを作らなくてすむ。統計的に、デ フォルトのthresholdだとテーブルを2倍にする時にクロー ンする必要があるのは約1/6。置き換わる古いノードは readerスレッドがすぐにテーブルを走査することで参照さ れなくなりGC対象となる。
  68. 68. Segment#clearvoid 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(); } }}
  69. 69. Segment#replaceV 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(); }}
  70. 70. Segment#replace その2boolean 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(); }}
  71. 71. ConcurrentHashMap#isEmptypublic 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; }...
  72. 72. ConcurrentHashMap#isEmpty 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;}
  73. 73. isEmptyのコメント超意訳• いつの時点でもテーブルが空にならなかった場合、ある segmentの一要素が追加され別で走査中に削除されるとい うABA問題(「Java並行処理プログラミング」15章参照)を 避けるために各segmentのmodCountを追跡する。他に ABA問題の影響を受ける可能性があるsize()や containsValue()メソッドでも同様にmodCountを使ってい る。
  74. 74. ConcurrentHashMap#size• まずはロック無しで2回までトライする • 数えている間に、別で変更処理が行われた らやり直す• 2回とも邪魔されてしまったら全Segmentに ロックをかけて数える
  75. 75. ConcurrentHashMap#size/** size()とcontainsValue()で使われる */static final int RETRIES_BEFORE_LOCK = 2; 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; ...
  76. 76. ConcurrentHashMap#size 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;}
  77. 77. ConcurrentHashMap#size 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;}
  78. 78. ConcurrentHashMap#containsValue ConcurrentHashMap#sizeと大体同じ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;...
  79. 79. ConcurrentHashMap#containsValue 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;}
  80. 80. ConcurrentHashMap#containsValue // 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;}
  81. 81. まとめ
  82. 82. まとめ• ConcurrentHashMapは、Segmentと HashEntryというデータ構造が肝• 高速化のために色んなことをやってる • finalでread時はロックしないように • hash値計算でちゃんとばらけさせる • ロック処理を最小限に抑える
  83. 83. ちなみに• ConcurrentHashMapより速い NonBlockingHashMapというのがある• Azul SystemsのCliff Click氏作 (DN$&6;OK)$P$&$QA/R$5F*. :L$@8M+0 :D$@8M+0 77768)*+.-./01.6591 ! ! &< HIE"" &< JKDE"" & & DE9F.G.05 DE9F.G.05 HIE>< :< :< : JKDE>< : < < HI JKD : & ! ; < = > ? : & ! ; < = > ? @AB08C. @AB08C. $ #$$$$%&!$()*+$,-./01.2$3456 !"
  84. 84. 詳しく知りたい人は• 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 Java http://sourceforge.net/projects/high-scale-lib
  85. 85. ご清聴ありがとう ございました!

×