我们都知道hashmap是非线程安全的。什么是非线程安全呢?个人理解,就是在多线程环境下,一个线程对值做变更时,不会立即同步到其他线程,这时候其他线程获取该值的话就是取到旧值,如果在此基础上进行计算的话就会得到错误的结果。
这点应该和java的内存模型有关,java内存分为工作内存和主内存。如图下所示(图为网上所找,侵删):
java所有的变量都存储在主内存中,工作内存保存的是主内存的副本,线程所有对变量的操作都是在工作内存中完成的,无法直接操作主内存。而线程写变量到工作内存,再由工作内存同步到主内存,这一过程并不是原子的,所以有可能你其中一个线程的对变量做了改变,但是变量的改变还没有同步到主内存,或者说其他线程没有从主内存获取到最新的值到该线程对应的工作内存的话就可能出现其他线程使用到的变量不是最新的。
基于以上两点,我们来看一下为什么多线程环境下hashmap会出现数据丢失的情况,它不是key value对应的吗?为什么key不同 还会出现数据丢失呢。下面我们来看看源码:
public HashMap(int initialCapacity, float loadFactor) {
if (initialCapacity < 0)
throw new IllegalArgumentException("Illegal initial capacity: " +
initialCapacity);
if (initialCapacity > MAXIMUM_CAPACITY)
initialCapacity = MAXIMUM_CAPACITY;
if (loadFactor <= 0 || Float.isNaN(loadFactor))
throw new IllegalArgumentException("Illegal load factor: " +
loadFactor);
this.loadFactor = loadFactor;
this.threshold = tableSizeFor(initialCapacity);
}
这是一段hashmap的构造方法,用来初始化负载因子和阈值。这里有个疑问,如果有小伙伴知道的话麻烦告诉我下,就是阈值的算法应该是tableSize * loadFactor,这里为什么直接赋值tableSize呢?tableSizeFor函数这里就不展开了,大概逻辑就是找到传入参数的最接近的下一个2的幂次方,举例说明就是:传入2返回2,传入3返回4。
接着再看下一段源码:
public V put(K key, V value) {
return putVal(hash(key), key, value, false, true);
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length; // 1.resize为扩容方法
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i]