CurrennthashMapソース解析
9509 ワード
何がconcurrenthashmapですか?
concurrenthashmap(略称chm)は、java 1.5が新しく導入したjava.util.co ncurrentパッケージのメンバーであり、hashtableの代わりとしています。なぜかというと、hashtableは全体の方法を同期させる構造を採用している。スレッドの安全を実現しましたが、性能が大幅に低下しました。hashmapです。同時進行の場合はミスしやすいです。したがって、安全を促進し、マルチスレッドで使用できるconcurrenthashmapです。
どのようにconcurrenthashmapを実現しますか?
まず構造方法を見てみましょう。これは一番下の構造方法です。
chmの位置付けはどうなりますか値を取得するには、まずどのsegmentを知ってからhashentryのindexを知る必要があります。最後のサイクルはindexの下の要素 を巡回します。
chm取得要素
1.hashmapのデフォルトサイズは1<<4、すなわち16ですが、concurrenthashmapは直接16.2.(k<
concurrenthashmap(略称chm)は、java 1.5が新しく導入したjava.util.co ncurrentパッケージのメンバーであり、hashtableの代わりとしています。なぜかというと、hashtableは全体の方法を同期させる構造を採用している。スレッドの安全を実現しましたが、性能が大幅に低下しました。hashmapです。同時進行の場合はミスしやすいです。したがって、安全を促進し、マルチスレッドで使用できるconcurrenthashmapです。
どのようにconcurrenthashmapを実現しますか?
まず構造方法を見てみましょう。これは一番下の構造方法です。
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;
// Find power-of-two sizes best matching arguments
int sshift = 0;
int ssize = 1;
while (ssize < concurrencyLevel) {
++sshift;// ssize
ssize <<= 1;
}
this.segmentShift = 32 - sshift;// , segment
this.segmentMask = ssize - 1;//segment
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
++c;
int cap = MIN_SEGMENT_TABLE_CAPACITY;
while (cap < c)
cap <<= 1;
// create segments and segments[0]
Segment s0 =
new Segment(loadFactor, (int)(cap * loadFactor),
(HashEntry[])new HashEntry[cap]);
Segment[] ss = (Segment[])new Segment[ssize];
UNSAFE.putOrderedObject(ss, SBASE, s0); // ordered write of segments[0]
this.segments = ss;
}
ここではhashmapと比較して分析したいのですが、彼らは似ているので、hashmapはentry[]であり、chmはsegmentsです。各segmentはhashmapであり、segmentに入るにはまだ対応する鍵が必要です。デフォルトのconcceurrenthasmapのsegment数は16です。各segment内のhashentry配列サイズも16です。threadshardは16*0.75です。chmの位置付けはどうなりますか
chm hash
private int hash(Object k) {
int h = hashSeed;
if ((0 != h) && (k instanceof String)) {
return sun.misc.Hashing.stringHash32((String) k);
}
h ^= k.hashCode();
// 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);
}
ここでkeyのhash値についてもう一回ハッシュしました。使う方法はwang/junnkinsのハッシュ・アルゴリズムで、ここでshはhash衝突を減らすためです。そうしないと、多くの数値が一つのsegmentにあります。このようにしないと、セグメントのロックの意味がなくなります。上記のコードはkeyの新しいhash値を算出しただけですが、このhash値でどうやって位置決めしますか? segment,:(h >>> segmentShift) & segmentMask。 h 4 ,segmentMask 15
index:(tab.length - 1) & h hashentry 1, sement h
:for (HashEntry e = (HashEntry) UNSAFE.getObjectVolatile
(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
e != null; e = e.next)
:if ((k = e.key) == key || (e.hash == h && key.equals(k)))
return e.value;
chm取得要素
public V get(Object key) {
Segment s; // manually integrate access methods to reduce overhead
HashEntry[] tab;
int h = hash(key);
long u = (((h >>> segmentShift) & segmentMask) << SSHIFT) + SBASE;
if ((s = (Segment)UNSAFE.getObjectVolatile(segments, u)) != null &&
(tab = s.table) != null) {
for (HashEntry e = (HashEntry) UNSAFE.getObjectVolatile
(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);
e != null; e = e.next) {
K k;
if ((k = e.key) == key || (e.hash == h && key.equals(k)))
return e.value;
}
}
return null;
}
値を取得するには、まずどのsegmentを知ってからhashentryのindexを知って、最後のサイクルはindexの下の要素を遍歴する必要があります。 segment,:(h >>> segmentShift) & segmentMask。 h 4 ,segmentMask 15
index:(tab.length - 1) & h hashentry 1, sement h
:for (HashEntry e = (HashEntry) UNSAFE.getObjectVolatile(tab, ((long)(((tab.length - 1) & h)) << TSHIFT) + TBASE);e != null; e = e.next)
:if ((k = e.key) == key || (e.hash == h && key.equals(k)))
return e.value;
chm保存元素 public V put(K key, V value) {
Segment s;
if (value == null)
throw new NullPointerException();
int hash = hash(key);
int j = (hash >>> segmentShift) & segmentMask;
if ((s = (Segment)UNSAFE.getObject // nonvolatile; recheck
(segments, (j << SSHIFT) + SBASE)) == null) // in ensureSegment
s = ensureSegment(j);
return s.put(key, hash, value, false);
}
jdk ,native , openjdk 。 put
(threadshold) , hashmap ,
concurrenthashmap ,
concrrenthasmap容量判断public int size() {
final Segment[] segments = this.segments;
int size;
boolean overflow; // true if size overflows 32 bits
long sum; // sum of modCounts
long last = 0L; // previous sum
int retries = -1; // first iteration isn't retry
try {
for (;;) {
if (retries++ == RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
ensureSegment(j).lock(); // force creation
}
sum = 0L;
size = 0;
overflow = false;
for (int j = 0; j < segments.length; ++j) {
Segment seg = segmentAt(segments, j);
if (seg != null) {
sum += seg.modCount;
int c = seg.count;
if (c < 0 || (size += c) < 0)
overflow = true;
}
}
if (sum == last)
break;
last = sum;
}
} finally {
if (retries > RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
segmentAt(segments, j).unlock();
}
}
return overflow ? Integer.MAX_VALUE : size;
}
, concurrenthashmap , , , put,remove
, , , 。 。 ,
chmが空かどうかの判断public boolean isEmpty() {
long sum = 0L;
final Segment[] segments = this.segments;
for (int j = 0; j < segments.length; ++j) {
Segment seg = segmentAt(segments, j);
if (seg != null) {
if (seg.count != 0)
return false;
sum += seg.modCount;
}
}
if (sum != 0L) { // recheck unless no modifications
for (int j = 0; j < segments.length; ++j) {
Segment seg = segmentAt(segments, j);
if (seg != null) {
if (seg.count != 0)
return false;
sum -= seg.modCount;
}
}
if (sum != 0L)
return false;
}
return true;
}
segment , ,count ,
modcount , , 。
chm削除要素final V remove(Object key, int hash, Object value) {
if (!tryLock())
scanAndLock(key, hash);
V oldValue = null;
try {
HashEntry[] tab = table;
int index = (tab.length - 1) & hash;
HashEntry e = entryAt(tab, index);
HashEntry pred = null;
while (e != null) {
K k;
HashEntry next = e.next;
if ((k = e.key) == key ||
(e.hash == hash && key.equals(k))) {
V v = e.value;
if (value == null || value == v || value.equals(v)) {
if (pred == null)
setEntryAt(tab, index, next);
else
pred.setNext(next);
++modCount;
--count;
oldValue = v;
}
break;
}
pred = e;
e = next;
}
} finally {
unlock();
}
return oldValue;
}
思考1.hashmapのデフォルトサイズは1<<4、すなわち16ですが、concurrenthashmapは直接16.2.(k<