Update: 很多设计都是类似的, 比如Hashmap
, ThreadLocalMap
, LongAdder
的Cell
都是设计为2^n
, 归根到底都是因为要hash寻址; CAS也是,不少地方用到来不挂起线程等待锁,不过很耗时,所以LongAdder
中用空间换时间创建一个数组,这个跟dp的思路相同;而创建数组的过程中对多线程只创建一次,用DCL校验的做法,和多线程的单例模式一样。不得不说还是要理解思想,有了这些想法,各种数据结构就都懂为什么这样写了,并且还可以自己设计。
原文: threadlocalmap, hashmap哈希和寻址异同随想
异: hash算法
- hashmap:
(h = key.hashCode()) ^ (h >>> 16);
- threadlocalmap:
(i+1) * HASH_INCREMENT & (len-1)
(HASH_INCREMENT = 0x61c88647
/1640531527
)
也就是一个是高低位异或,另一个是每次增加1640531527。为什么是1640531527? 看看这个数和int最大值的关系,并且注意一下结尾的数字7联想hash规则,就知道了。
get
碰撞:
- hashmap:
next
- threadlocalmap:
getEntryAfterMiss
一个顺着linkedlist找,另一个往后index找, 这是两种map内存的结构不同决定的: hashmap存的Node
和threadlocalmap存的Entry
(有无next
):
static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
...
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
同: hash寻址:
- hashmap:
first = tab[(n - 1) & hash]
- threadlocalmap:
i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
, 其中threadLocalHashCode = nextHashCode();
,private static int nextHashCode() { return nextHashCode.getAndAdd(HASH_INCREMENT); }
都是&
上len - 1
至于为什么threadLocal要用自定义的threadlocalmap附着在每个thread上面,因为:
- 限定key只能为threadlocal类型;
- key作为
Entry
的第一个field,static class Entry extends WeakReference<ThreadLocal<?>>
, 所以key也是WeakReference会很容易GC,这样来回收内存(虽然不完全) - 自定义的
get
/set
有清理过期数据功能
暂时突然想到这么多。感想: threadlocalmap比hashmap还是要简单的