SlideShare a Scribd company logo
ConcurrentHashMap
   Code Reading
 角田 直行 <kakuda@gmail.com>
Agenda

• 準備
• Overview
• 読む
• まとめ
準備
準備するもの
•   Eclipse

•   ソース

    •   ConcurrentHashMap.java

    •   テストケース

        •   JSR166TestCase.java

        •   ConcurrentHashMapTest.java

•   ConcurrentHashMapに関する参照資料

•   気合い
ソースの取得場所



• ConcurrentHashMap.javaはJDKより
• テストケースはDoug Leaさんのサイトより
 http://gee.cs.oswego.edu/dl/concurrency-interest/
参照した資料

•   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
Overview
Overview

•   Constants: 6つ

•   Field: 6つ

•   ユーティリティ: 2つ(hash(), segmentFor())

•   インナークラス: 2つ(HashEntry, Segment)

•   Public メソッド: 24コ(Constructorは5つ)

•   Iteratorサポート: 8つ

•   Serializationサポート: 2つ(writeObject(), readObject())
多分重要なトコ

•   Constants: 6つ

•   Field: 6つ

•   ユーティリティ: 2つ(hash(), segmentFor())

•   インナークラス: 2つ(HashEntry, Segment)

•   Public メソッド: 24コ(Constructorは5つ)

•   Iteratorサポート: 8つ

•   Serializationサポート: 2つ(writeObject(), readObject())
今回は割愛するトコ

•   Constants: 6つ

•   Field: 6つ

•   ユーティリティ: 2つ(hash(), segmentFor())

•   インナークラス: 2つ(HashEntry, Segment)

•   Public メソッド: 24コ(Constructorは5つ)

•   Iteratorサポート: 8つ

•   Serializationサポート: 2つ(writeObject(), readObject())
読む
コンストラクタ
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);
}
コンストラクタ


/** デフォルトの初期容量 */
static final int DEFAULT_INITIAL_CAPACITY = 16;
/** デフォルトの負荷係数 */
static final float DEFAULT_LOAD_FACTOR = 0.75f;
/** デフォルトの並行処理レベル */
static final int DEFAULT_CONCURRENCY_LEVEL = 16;
コンストラクタ
/** 最大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;
  ...
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);
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);
}
Segmentとは

• ConcurrentHashMap.javaにて定義されて
 いるインナークラス

 • 1つのConcurrentHashMapインスタンス
  に複数(デフォルト16)のSegmentを持つ

• ハッシュテーブルの特別バージョン
• ReentrantLockのサブクラス
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;
  }
HashEntryとは

• ConcurrentHashMap.javaにて定義されて
 いるインナークラス

 • 1つのSegmentインスタンスに1つ以上の
   HashEntryを持つ

• フィールドにkey, hash値, value, next(次の
 HashEntry)を持つ

 • key, hash, nextはfinal
HashEntry
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];
  }
}
Segment、HashEntryの構造
Segment、HashEntryの構造




     ConcurrentHashMap
Segment、HashEntryの構造


           Segment



           Segment

      ConcurrentHashMap
  ・
  ・
  ・
           Segment
Segment、HashEntryの構造


 Hash
 Entry
              Segment



              Segment

         ConcurrentHashMap
    ・
    ・
    ・
              Segment
Segment、HashEntryの構造


 Hash
 Entry
              Segment



 Hash
 Entry
              Segment

         ConcurrentHashMap
    ・
    ・
    ・
              Segment
Segment、HashEntryの構造


 Hash       Hash
 Entry
               Segment
            Entry



 Hash
 Entry
              Segment

         ConcurrentHashMap
    ・
    ・
    ・
              Segment
Segment、HashEntryの構造


 Hash       Hash
 Entry
               Segment
            Entry



 Hash       Hash
 Entry
               Segment
            Entry

         ConcurrentHashMap
    ・
    ・
    ・
              Segment
Segment、HashEntryの構造


 Hash       Hash         Hash
 Entry
               Segment
            Entry        Entry



 Hash       Hash
 Entry
               Segment
            Entry

         ConcurrentHashMap
    ・
    ・
    ・
              Segment
Segment、HashEntryの構造


 Hash       Hash         Hash
 Entry
               Segment
            Entry        Entry
           実際のチェーン数は
              1∼2個くらい
 Hash       Hash
 Entry
               Segment
            Entry

         ConcurrentHashMap
    ・
    ・
    ・
              Segment
Segment、HashEntryの構造


 Hash       Hash         Hash
 Entry
               Segment
            Entry        Entry
           実際のチェーン数は
              1∼2個くらい
 Hash       Hash
 Entry
               Segment
            Entry

         ConcurrentHashMap
    ・
    ・
    ・
 Hash
 Entry
              Segment
Segment、HashEntryの構造


 Hash       Hash         Hash
 Entry
               Segment
            Entry        Entry
           実際のチェーン数は
              1∼2個くらい
 Hash       Hash
 Entry
               Segment
            Entry

         ConcurrentHashMap
    ・
    ・          全Segmentが埋まったらSegment
    ・           のサイズは2倍に拡張される
 Hash
 Entry
              Segment
ConcurrentHashMap#get


public V get(Object key) {
  // 与えられたハッシュ値を元にちゃんとしたハッシュ値を求める
    int hash = hash(key.hashCode());
    // ハッシュ値に該当するセグメントを見つけSegment#getをcall
    return segmentFor(hash).get(key, hash);
}
他の主要メソッドも
                           似たような処理
                                                          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);
                                                          }
他の主要メソッドも
                           似たような処理
                                                          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);
                                                          }
ConcurrentHashMap#hash

    何やっているかわかりません・・・orz
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);
}

    HashMap#hashよりBetterな実装に置き換えてる

 http://www.concentric.net/ Ttwang/tech/inthash.htm が参考ソース?
いいハッシュ値を返すと・・・


              Segment



              Segment

         ConcurrentHashMap
     ・
     ・
     ・
              Segment
いいハッシュ値を返すと・・・


                           Segment


(“1”, “a”)
                           Segment

 PUT                  ConcurrentHashMap
                  ・
                  ・
                  ・
                           Segment
いいハッシュ値を返すと・・・


   (“1”, “a”)        Segment



                     Segment

                ConcurrentHashMap
        ・
        ・
        ・
                     Segment
いいハッシュ値を返すと・・・


                (“1”, “a”)        Segment


(“2”, “b”)
                                  Segment

 PUT                         ConcurrentHashMap
                     ・
                     ・
                     ・
                                  Segment
いいハッシュ値を返すと・・・


   (“1”, “a”)        Segment



   (“2”, “b”)        Segment

                ConcurrentHashMap
        ・
        ・
        ・
                     Segment
いいハッシュ値を返すと・・・


                (“1”, “a”)        Segment


(“3”, “c”)
                (“2”, “b”)        Segment

PUT                          ConcurrentHashMap
                     ・
                     ・
                     ・
                                  Segment
いいハッシュ値を返すと・・・


   (“1”, “a”)        Segment



   (“2”, “b”)        Segment

                ConcurrentHashMap
        ・
        ・
        ・
   (“3”, “c”)        Segment
いいハッシュ値を返すと・・・


     (“1”, “a”)   Segment



     (“2”, “b”)   Segment

ちゃんとバラけてくれる!
      ConcurrentHashMap
          ・
          ・
          ・
     (“3”, “c”)   Segment
悪いハッシュ値を返すと・・・


              Segment



              Segment

         ConcurrentHashMap
     ・
     ・
     ・
              Segment
悪いハッシュ値を返すと・・・


                           Segment


(“1”, “a”)
                           Segment

 PUT                  ConcurrentHashMap
                  ・
                  ・
                  ・
                           Segment
悪いハッシュ値を返すと・・・


   (“1”, “a”)        Segment



                     Segment

                ConcurrentHashMap
        ・
        ・
        ・
                     Segment
悪いハッシュ値を返すと・・・


                (“1”, “a”)        Segment


(“2”, “b”)
                                  Segment

 PUT                         ConcurrentHashMap
                     ・
                     ・
                     ・
                                  Segment
悪いハッシュ値を返すと・・・


   (“1”, “a”)    (“2”, Segment
                       “b”)




                     Segment

                ConcurrentHashMap
        ・
        ・
        ・
                     Segment
悪いハッシュ値を返すと・・・


                (“1”, “a”)    (“2”, Segment
                                    “b”)



(“3”, “c”)
                                  Segment

PUT                          ConcurrentHashMap
                     ・
                     ・
                     ・
                                  Segment
悪いハッシュ値を返すと・・・


   (“1”, “a”)    (“2”, Segment
                       “b”)      (“3”, “c”)




                     Segment

                ConcurrentHashMap
        ・
        ・
        ・
                     Segment
悪いハッシュ値を返すと・・・


   (“1”, “a”)    (“2”, Segment
                       “b”)      (“3”, “c”)




                     Segment
   偏ってしまい、
                ConcurrentHashMap
    ・
すごく効率が悪くなる!
        ・
        ・
                     Segment
Segment#get
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#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;
}
Segment#containsValue
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#put

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
      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#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;
...
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();
    }
}
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();
    }
}
Segment#remove

• HashEntryにはnextという次の要素を表す
 フィールドがある

• removeが行われるとnextの付け替えが発生
 する

• nextフィールドはfinalなので削除した要素よ
 り前を作り直さなければならない
A          B          C          D            E
                      Segment
Next = B   Next = C   Next = D   Next = E   Next = null
A          B            C           D            E
                         Segment
Next = B   Next = C     Next = D    Next = E   Next = null




                      remove(“C”)
A          B                    D            E
                      Segment
Next = B   Next = C             Next = E   Next = null
Nextがfinalなので
  A          B        Dに変更できない!D               E
                       Segment
Next = B   Next = C             Next = E   Next = null
Nextがfinalなので
  A          B        Dに変更できない!D               E
                       Segment
Next = B   Next = C             Next = E   Next = null




              AとBは作り直し
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
...
コメントの超意訳

•   各リストのノードを新しいMapに再分類する。2つの強力
    な拡張(SegmentとHashEntry)を使っているため、同じ
    indexを保つか2つのoffsetを同時に移動させなければなら
    ない。nextフィールドは変わらないので、古いノードは再
    利用でき不必要なノードを作らなくてすむ。統計的に、デ
    フォルトのthresholdだとテーブルを2倍にする時にクロー
    ンする必要があるのは約1/6。置き換わる古いノードは
    readerスレッドがすぐにテーブルを走査することで参照さ
    れなくなりGC対象となる。
Segment#clear
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#replace
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 その2
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();
    }
}
ConcurrentHashMap#isEmpty


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



    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;
}
isEmptyのコメント超意訳


•   いつの時点でもテーブルが空にならなかった場合、ある
    segmentの一要素が追加され別で走査中に削除されるとい
    うABA問題(「Java並行処理プログラミング」15章参照)を
    避けるために各segmentのmodCountを追跡する。他に
    ABA問題の影響を受ける可能性があるsize()や
    containsValue()メソッドでも同様にmodCountを使ってい
    る。
ConcurrentHashMap#size


• まずはロック無しで2回までトライする
 • 数えている間に、別で変更処理が行われた
  らやり直す

• 2回とも邪魔されてしまったら全Segmentに
 ロックをかけて数える
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;
  ...
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;
}
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;
}
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;
...
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;
}
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;
}
まとめ
まとめ

• ConcurrentHashMapは、Segmentと
 HashEntryというデータ構造が肝

• 高速化のために色んなことをやってる
 • finalでread時はロックしないように
 • hash値計算でちゃんとばらけさせる
 • ロック処理を最小限に抑える
ちなみに


• 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
                       !"
詳しく知りたい人は


•   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
ご清聴ありがとう
 ございました!

More Related Content

Recently uploaded

FIDO Alliance Osaka Seminar: Welcome Slides.pdf
FIDO Alliance Osaka Seminar: Welcome Slides.pdfFIDO Alliance Osaka Seminar: Welcome Slides.pdf
FIDO Alliance Osaka Seminar: Welcome Slides.pdfFIDO Alliance
 
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアルLoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアルCRI Japan, Inc.
 
【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matching
【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matching【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matching
【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matchingharmonylab
 
論文紹介: Exploiting semantic segmentation to boost reinforcement learning in vid...
論文紹介: Exploiting semantic segmentation to boost reinforcement learning in vid...論文紹介: Exploiting semantic segmentation to boost reinforcement learning in vid...
論文紹介: Exploiting semantic segmentation to boost reinforcement learning in vid...atsushi061452
 
FIDO Alliance Osaka Seminar: NEC & Yubico Panel.pdf
FIDO Alliance Osaka Seminar: NEC & Yubico Panel.pdfFIDO Alliance Osaka Seminar: NEC & Yubico Panel.pdf
FIDO Alliance Osaka Seminar: NEC & Yubico Panel.pdfFIDO Alliance
 
FIDO Alliance Osaka Seminar: LY-DOCOMO-KDDI-Mercari Panel.pdf
FIDO Alliance Osaka Seminar: LY-DOCOMO-KDDI-Mercari Panel.pdfFIDO Alliance Osaka Seminar: LY-DOCOMO-KDDI-Mercari Panel.pdf
FIDO Alliance Osaka Seminar: LY-DOCOMO-KDDI-Mercari Panel.pdfFIDO Alliance
 
YugabyteDB適用に向けた取り組みと隠れた魅力 (DSS Asia 2024 発表資料)
YugabyteDB適用に向けた取り組みと隠れた魅力 (DSS Asia 2024 発表資料)YugabyteDB適用に向けた取り組みと隠れた魅力 (DSS Asia 2024 発表資料)
YugabyteDB適用に向けた取り組みと隠れた魅力 (DSS Asia 2024 発表資料)NTT DATA Technology & Innovation
 
MPAなWebフレームワーク、Astroの紹介 (その2) 2024/05/24の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その2) 2024/05/24の勉強会で発表されたものです。MPAなWebフレームワーク、Astroの紹介 (その2) 2024/05/24の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その2) 2024/05/24の勉強会で発表されたものです。iPride Co., Ltd.
 
FIDO Alliance Osaka Seminar: CloudGate.pdf
FIDO Alliance Osaka Seminar: CloudGate.pdfFIDO Alliance Osaka Seminar: CloudGate.pdf
FIDO Alliance Osaka Seminar: CloudGate.pdfFIDO Alliance
 
2024年5月25日Serverless Meetup大阪 アプリケーションをどこで動かすべきなのか.pptx
2024年5月25日Serverless Meetup大阪 アプリケーションをどこで動かすべきなのか.pptx2024年5月25日Serverless Meetup大阪 アプリケーションをどこで動かすべきなのか.pptx
2024年5月25日Serverless Meetup大阪 アプリケーションをどこで動かすべきなのか.pptxssuserbefd24
 
2024年度_サイバーエージェント_新卒研修「データベースの歴史」.pptx
2024年度_サイバーエージェント_新卒研修「データベースの歴史」.pptx2024年度_サイバーエージェント_新卒研修「データベースの歴史」.pptx
2024年度_サイバーエージェント_新卒研修「データベースの歴史」.pptxyassun7010
 
論文紹介: Offline Q-Learning on diverse Multi-Task data both scales and generalizes
論文紹介: Offline Q-Learning on diverse Multi-Task data both scales and generalizes論文紹介: Offline Q-Learning on diverse Multi-Task data both scales and generalizes
論文紹介: Offline Q-Learning on diverse Multi-Task data both scales and generalizesatsushi061452
 
FIDO Alliance Osaka Seminar: PlayStation Passkey Deployment Case Study.pdf
FIDO Alliance Osaka Seminar: PlayStation Passkey Deployment Case Study.pdfFIDO Alliance Osaka Seminar: PlayStation Passkey Deployment Case Study.pdf
FIDO Alliance Osaka Seminar: PlayStation Passkey Deployment Case Study.pdfFIDO Alliance
 

Recently uploaded (14)

FIDO Alliance Osaka Seminar: Welcome Slides.pdf
FIDO Alliance Osaka Seminar: Welcome Slides.pdfFIDO Alliance Osaka Seminar: Welcome Slides.pdf
FIDO Alliance Osaka Seminar: Welcome Slides.pdf
 
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアルLoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
LoRaWAN 4チャンネル電流センサー・コンバーター CS01-LB 日本語マニュアル
 
【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matching
【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matching【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matching
【DLゼミ】XFeat: Accelerated Features for Lightweight Image Matching
 
論文紹介: Exploiting semantic segmentation to boost reinforcement learning in vid...
論文紹介: Exploiting semantic segmentation to boost reinforcement learning in vid...論文紹介: Exploiting semantic segmentation to boost reinforcement learning in vid...
論文紹介: Exploiting semantic segmentation to boost reinforcement learning in vid...
 
FIDO Alliance Osaka Seminar: NEC & Yubico Panel.pdf
FIDO Alliance Osaka Seminar: NEC & Yubico Panel.pdfFIDO Alliance Osaka Seminar: NEC & Yubico Panel.pdf
FIDO Alliance Osaka Seminar: NEC & Yubico Panel.pdf
 
FIDO Alliance Osaka Seminar: LY-DOCOMO-KDDI-Mercari Panel.pdf
FIDO Alliance Osaka Seminar: LY-DOCOMO-KDDI-Mercari Panel.pdfFIDO Alliance Osaka Seminar: LY-DOCOMO-KDDI-Mercari Panel.pdf
FIDO Alliance Osaka Seminar: LY-DOCOMO-KDDI-Mercari Panel.pdf
 
YugabyteDB適用に向けた取り組みと隠れた魅力 (DSS Asia 2024 発表資料)
YugabyteDB適用に向けた取り組みと隠れた魅力 (DSS Asia 2024 発表資料)YugabyteDB適用に向けた取り組みと隠れた魅力 (DSS Asia 2024 発表資料)
YugabyteDB適用に向けた取り組みと隠れた魅力 (DSS Asia 2024 発表資料)
 
MPAなWebフレームワーク、Astroの紹介 (その2) 2024/05/24の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その2) 2024/05/24の勉強会で発表されたものです。MPAなWebフレームワーク、Astroの紹介 (その2) 2024/05/24の勉強会で発表されたものです。
MPAなWebフレームワーク、Astroの紹介 (その2) 2024/05/24の勉強会で発表されたものです。
 
FIDO Alliance Osaka Seminar: CloudGate.pdf
FIDO Alliance Osaka Seminar: CloudGate.pdfFIDO Alliance Osaka Seminar: CloudGate.pdf
FIDO Alliance Osaka Seminar: CloudGate.pdf
 
2024年5月25日Serverless Meetup大阪 アプリケーションをどこで動かすべきなのか.pptx
2024年5月25日Serverless Meetup大阪 アプリケーションをどこで動かすべきなのか.pptx2024年5月25日Serverless Meetup大阪 アプリケーションをどこで動かすべきなのか.pptx
2024年5月25日Serverless Meetup大阪 アプリケーションをどこで動かすべきなのか.pptx
 
2024年度_サイバーエージェント_新卒研修「データベースの歴史」.pptx
2024年度_サイバーエージェント_新卒研修「データベースの歴史」.pptx2024年度_サイバーエージェント_新卒研修「データベースの歴史」.pptx
2024年度_サイバーエージェント_新卒研修「データベースの歴史」.pptx
 
【AI論文解説】Consistency ModelとRectified Flow
【AI論文解説】Consistency ModelとRectified Flow【AI論文解説】Consistency ModelとRectified Flow
【AI論文解説】Consistency ModelとRectified Flow
 
論文紹介: Offline Q-Learning on diverse Multi-Task data both scales and generalizes
論文紹介: Offline Q-Learning on diverse Multi-Task data both scales and generalizes論文紹介: Offline Q-Learning on diverse Multi-Task data both scales and generalizes
論文紹介: Offline Q-Learning on diverse Multi-Task data both scales and generalizes
 
FIDO Alliance Osaka Seminar: PlayStation Passkey Deployment Case Study.pdf
FIDO Alliance Osaka Seminar: PlayStation Passkey Deployment Case Study.pdfFIDO Alliance Osaka Seminar: PlayStation Passkey Deployment Case Study.pdf
FIDO Alliance Osaka Seminar: PlayStation Passkey Deployment Case Study.pdf
 

Featured

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by HubspotMarius Sescu
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTExpeed Software
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsPixeldarts
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthThinkNow
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfmarketingartwork
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024Neil Kimberley
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)contently
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024Albert Qian
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsKurio // The Social Media Age(ncy)
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Search Engine Journal
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summarySpeakerHub
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next Tessa Mero
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentLily Ray
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best PracticesVit Horky
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project managementMindGenius
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...RachelPearson36
 

Featured (20)

2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot2024 State of Marketing Report – by Hubspot
2024 State of Marketing Report – by Hubspot
 
Everything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPTEverything You Need To Know About ChatGPT
Everything You Need To Know About ChatGPT
 
Product Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage EngineeringsProduct Design Trends in 2024 | Teenage Engineerings
Product Design Trends in 2024 | Teenage Engineerings
 
How Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental HealthHow Race, Age and Gender Shape Attitudes Towards Mental Health
How Race, Age and Gender Shape Attitudes Towards Mental Health
 
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdfAI Trends in Creative Operations 2024 by Artwork Flow.pdf
AI Trends in Creative Operations 2024 by Artwork Flow.pdf
 
Skeleton Culture Code
Skeleton Culture CodeSkeleton Culture Code
Skeleton Culture Code
 
PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024PEPSICO Presentation to CAGNY Conference Feb 2024
PEPSICO Presentation to CAGNY Conference Feb 2024
 
Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)Content Methodology: A Best Practices Report (Webinar)
Content Methodology: A Best Practices Report (Webinar)
 
How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024How to Prepare For a Successful Job Search for 2024
How to Prepare For a Successful Job Search for 2024
 
Social Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie InsightsSocial Media Marketing Trends 2024 // The Global Indie Insights
Social Media Marketing Trends 2024 // The Global Indie Insights
 
Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024Trends In Paid Search: Navigating The Digital Landscape In 2024
Trends In Paid Search: Navigating The Digital Landscape In 2024
 
5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary5 Public speaking tips from TED - Visualized summary
5 Public speaking tips from TED - Visualized summary
 
ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd ChatGPT and the Future of Work - Clark Boyd
ChatGPT and the Future of Work - Clark Boyd
 
Getting into the tech field. what next
Getting into the tech field. what next Getting into the tech field. what next
Getting into the tech field. what next
 
Google's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search IntentGoogle's Just Not That Into You: Understanding Core Updates & Search Intent
Google's Just Not That Into You: Understanding Core Updates & Search Intent
 
How to have difficult conversations
How to have difficult conversations How to have difficult conversations
How to have difficult conversations
 
Introduction to Data Science
Introduction to Data ScienceIntroduction to Data Science
Introduction to Data Science
 
Time Management & Productivity - Best Practices
Time Management & Productivity -  Best PracticesTime Management & Productivity -  Best Practices
Time Management & Productivity - Best Practices
 
The six step guide to practical project management
The six step guide to practical project managementThe six step guide to practical project management
The six step guide to practical project management
 
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
Beginners Guide to TikTok for Search - Rachel Pearson - We are Tilt __ Bright...
 

ConcurrentHashMap Code Reading

  • 1. ConcurrentHashMap Code Reading 角田 直行 <kakuda@gmail.com>
  • 2. Agenda • 準備 • Overview • 読む • まとめ
  • 4. 準備するもの • Eclipse • ソース • ConcurrentHashMap.java • テストケース • JSR166TestCase.java • ConcurrentHashMapTest.java • ConcurrentHashMapに関する参照資料 • 気合い
  • 5. ソースの取得場所 • ConcurrentHashMap.javaはJDKより • テストケースはDoug Leaさんのサイトより http://gee.cs.oswego.edu/dl/concurrency-interest/
  • 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
  • 8. Overview • Constants: 6つ • Field: 6つ • ユーティリティ: 2つ(hash(), segmentFor()) • インナークラス: 2つ(HashEntry, Segment) • Public メソッド: 24コ(Constructorは5つ) • Iteratorサポート: 8つ • Serializationサポート: 2つ(writeObject(), readObject())
  • 9. 多分重要なトコ • Constants: 6つ • Field: 6つ • ユーティリティ: 2つ(hash(), segmentFor()) • インナークラス: 2つ(HashEntry, Segment) • Public メソッド: 24コ(Constructorは5つ) • Iteratorサポート: 8つ • Serializationサポート: 2つ(writeObject(), readObject())
  • 10. 今回は割愛するトコ • Constants: 6つ • Field: 6つ • ユーティリティ: 2つ(hash(), segmentFor()) • インナークラス: 2つ(HashEntry, Segment) • Public メソッド: 24コ(Constructorは5つ) • Iteratorサポート: 8つ • Serializationサポート: 2つ(writeObject(), readObject())
  • 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. コンストラクタ /** デフォルトの初期容量 */ static final int DEFAULT_INITIAL_CAPACITY = 16; /** デフォルトの負荷係数 */ static final float DEFAULT_LOAD_FACTOR = 0.75f; /** デフォルトの並行処理レベル */ static final int DEFAULT_CONCURRENCY_LEVEL = 16;
  • 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. 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. 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. Segmentとは • ConcurrentHashMap.javaにて定義されて いるインナークラス • 1つのConcurrentHashMapインスタンス に複数(デフォルト16)のSegmentを持つ • ハッシュテーブルの特別バージョン • ReentrantLockのサブクラス
  • 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. HashEntryとは • ConcurrentHashMap.javaにて定義されて いるインナークラス • 1つのSegmentインスタンスに1つ以上の HashEntryを持つ • フィールドにkey, hash値, value, next(次の HashEntry)を持つ • key, hash, nextはfinal
  • 20. HashEntry 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]; } }
  • 22. Segment、HashEntryの構造 ConcurrentHashMap
  • 23. Segment、HashEntryの構造 Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  • 24. Segment、HashEntryの構造 Hash Entry Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  • 25. Segment、HashEntryの構造 Hash Entry Segment Hash Entry Segment ConcurrentHashMap ・ ・ ・ Segment
  • 26. Segment、HashEntryの構造 Hash Hash Entry Segment Entry Hash Entry Segment ConcurrentHashMap ・ ・ ・ Segment
  • 27. Segment、HashEntryの構造 Hash Hash Entry Segment Entry Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ ・ Segment
  • 28. Segment、HashEntryの構造 Hash Hash Hash Entry Segment Entry Entry Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ ・ Segment
  • 29. Segment、HashEntryの構造 Hash Hash Hash Entry Segment Entry Entry 実際のチェーン数は 1∼2個くらい Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ ・ Segment
  • 30. Segment、HashEntryの構造 Hash Hash Hash Entry Segment Entry Entry 実際のチェーン数は 1∼2個くらい Hash Hash Entry Segment Entry ConcurrentHashMap ・ ・ ・ Hash Entry Segment
  • 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. ConcurrentHashMap#get public V get(Object key) { // 与えられたハッシュ値を元にちゃんとしたハッシュ値を求める int hash = hash(key.hashCode()); // ハッシュ値に該当するセグメントを見つけSegment#getをcall return segmentFor(hash).get(key, hash); }
  • 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. 他の主要メソッドも 似たような処理 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. ConcurrentHashMap#hash 何やっているかわかりません・・・orz 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); } HashMap#hashよりBetterな実装に置き換えてる http://www.concentric.net/ Ttwang/tech/inthash.htm が参考ソース?
  • 36. いいハッシュ値を返すと・・・ Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  • 37. いいハッシュ値を返すと・・・ Segment (“1”, “a”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  • 38. いいハッシュ値を返すと・・・ (“1”, “a”) Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  • 39. いいハッシュ値を返すと・・・ (“1”, “a”) Segment (“2”, “b”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  • 40. いいハッシュ値を返すと・・・ (“1”, “a”) Segment (“2”, “b”) Segment ConcurrentHashMap ・ ・ ・ Segment
  • 41. いいハッシュ値を返すと・・・ (“1”, “a”) Segment (“3”, “c”) (“2”, “b”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  • 42. いいハッシュ値を返すと・・・ (“1”, “a”) Segment (“2”, “b”) Segment ConcurrentHashMap ・ ・ ・ (“3”, “c”) Segment
  • 43. いいハッシュ値を返すと・・・ (“1”, “a”) Segment (“2”, “b”) Segment ちゃんとバラけてくれる! ConcurrentHashMap ・ ・ ・ (“3”, “c”) Segment
  • 44. 悪いハッシュ値を返すと・・・ Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  • 45. 悪いハッシュ値を返すと・・・ Segment (“1”, “a”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  • 46. 悪いハッシュ値を返すと・・・ (“1”, “a”) Segment Segment ConcurrentHashMap ・ ・ ・ Segment
  • 47. 悪いハッシュ値を返すと・・・ (“1”, “a”) Segment (“2”, “b”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  • 48. 悪いハッシュ値を返すと・・・ (“1”, “a”) (“2”, Segment “b”) Segment ConcurrentHashMap ・ ・ ・ Segment
  • 49. 悪いハッシュ値を返すと・・・ (“1”, “a”) (“2”, Segment “b”) (“3”, “c”) Segment PUT ConcurrentHashMap ・ ・ ・ Segment
  • 50. 悪いハッシュ値を返すと・・・ (“1”, “a”) (“2”, Segment “b”) (“3”, “c”) Segment ConcurrentHashMap ・ ・ ・ Segment
  • 51. 悪いハッシュ値を返すと・・・ (“1”, “a”) (“2”, Segment “b”) (“3”, “c”) Segment 偏ってしまい、 ConcurrentHashMap ・ すごく効率が悪くなる! ・ ・ Segment
  • 52. Segment#get 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; }
  • 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. Segment#containsValue 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; }
  • 55. Segment#put 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; ...
  • 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. 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. 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. 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. Segment#remove • HashEntryにはnextという次の要素を表す フィールドがある • removeが行われるとnextの付け替えが発生 する • nextフィールドはfinalなので削除した要素よ り前を作り直さなければならない
  • 61. A B C D E Segment Next = B Next = C Next = D Next = E Next = null
  • 62. A B C D E Segment Next = B Next = C Next = D Next = E Next = null remove(“C”)
  • 63. A B D E Segment Next = B Next = C Next = E Next = null
  • 64. Nextがfinalなので A B Dに変更できない!D E Segment Next = B Next = C Next = E Next = null
  • 65. Nextがfinalなので A B Dに変更できない!D E Segment Next = B Next = C Next = E Next = null AとBは作り直し
  • 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. コメントの超意訳 • 各リストのノードを新しいMapに再分類する。2つの強力 な拡張(SegmentとHashEntry)を使っているため、同じ indexを保つか2つのoffsetを同時に移動させなければなら ない。nextフィールドは変わらないので、古いノードは再 利用でき不必要なノードを作らなくてすむ。統計的に、デ フォルトのthresholdだとテーブルを2倍にする時にクロー ンする必要があるのは約1/6。置き換わる古いノードは readerスレッドがすぐにテーブルを走査することで参照さ れなくなりGC対象となる。
  • 68. Segment#clear 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(); } } }
  • 69. Segment#replace 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(); } }
  • 70. Segment#replace その2 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(); } }
  • 71. ConcurrentHashMap#isEmpty 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; } ...
  • 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. isEmptyのコメント超意訳 • いつの時点でもテーブルが空にならなかった場合、ある segmentの一要素が追加され別で走査中に削除されるとい うABA問題(「Java並行処理プログラミング」15章参照)を 避けるために各segmentのmodCountを追跡する。他に ABA問題の影響を受ける可能性があるsize()や containsValue()メソッドでも同様にmodCountを使ってい る。
  • 74. ConcurrentHashMap#size • まずはロック無しで2回までトライする • 数えている間に、別で変更処理が行われた らやり直す • 2回とも邪魔されてしまったら全Segmentに ロックをかけて数える
  • 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. 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. 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. 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. 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. 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; }
  • 82. まとめ • ConcurrentHashMapは、Segmentと HashEntryというデータ構造が肝 • 高速化のために色んなことをやってる • finalでread時はロックしないように • hash値計算でちゃんとばらけさせる • ロック処理を最小限に抑える
  • 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. 詳しく知りたい人は • 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