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);
}
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];
}
}
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 が参考ソース?
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();
}
}
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が?nalなので
A B Dに変更できない!D E
Segment
Next = B Next = C Next = E Next = null
65. Nextが?nalなので
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
...
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;
}