一、HashMap死循环

  举例:当线程1执行时,线程2完成一次reHash,此时使得线程1中的e和next的关系为e == next.next,这样在线程1reHash完成后,就会产生循环链接,如果遍历查询就会进入死循环。(即头插法带来的问题
  JDK1.8中扩容时候使用了尾插法来解决了之前版本显而易见的死循环问题。但是HashMap的线程不安全问题还体现在数据丢失上,也就是多个线程在进行put操作的时候,存在数据的覆盖问题,所以依然是线程不安全的。另外,在实际使用中有人也确实发现了TreeNode父节点的相互引用还是会造成死循环的。总结:(1)死循环;(2)数据丢失
  HashMap死循环原理

二、一致性Hash算法

  凡是涉及到分布式的系统,就会有负载均衡和数据分布的问题。为了让连接(或者数据)能够分布得更均匀,很多时候会使用到Hash算法。

1.Hash取模 (hash(request) % n)

  方法:假设我们现在有3个服务器,想要做负载均衡,就可以对请求的ip地址或者用户的id等使用Hash函数,然后将计算得出的Hash值对3取模,余数为几,就把请求分配到相应的服务器上。
  缺点:如果新增服务器的时候,绝大多数请求基本上都需要重新映射到另一个节点。这种变动有时候是不能接受的。比如在Web负载均衡的场景下,session会保存在每个节点里。当然,如果你是“无状态”的服务,那不会存在这个问题。因为如果增加或者删除了一个节点,就会导致几乎所有的数据都需要重新迁移。

2.一致性Hash

  优点:解决因为横向伸缩导致的大规模数据变动。
  方法:上面说到用节点的数量作为除数去求余。而一致性Hash的除数是2^32。从0到2^32-1,首尾相连构成了一个环。我们先对服务器节点的IP进行Hash,然后除以2^32,得到服务器节点在这个Hash环中的位置。如果有请求进来了,同样进行Hash然后处于2^32求余。如果落在Hash环上,然后顺时针找到第一个节点,这个节点就负责处理这个请求。
  缺点:一致性Hash算法在节点数量较少的时候,会出现分布不均匀的问题。解决这个问题的方案就是在Hash环上增加虚拟节点

三、Hash函数处理冲突的方法

  以下方法常用的为方法1和方法2,其他方法通常不用。

1.开放定址法

  图片说明
  图片说明
  对增量di有三种取法:
  [1]线性探测再散列 di = 1 , 2 , 3 , … , m-1
  [2]平方探测再散列 di = 1 , -1 , 2, -2 , 4 , -4 , … , k , -k(取相应数的平方)
  [3]随机探测再散列 di 是一组伪随机数列

2.拉链法(HashMap使用的方法)

  将所有哈希地址相同的记录都放在同一链表中。HashMap底层实现数据结构为数组+链表的形式,JDK8及其以后的版本中使用了数组+链表+红黑树实现,解决了链表太长导致的查询速度变慢的问题。
图片说明

3.再哈希法

  产生冲突时,用其他哈希函数计算,直到不再产生冲突位置。

4.建立一个公共溢出区,把冲突的节点全部放入

  把冲突的节点全部放入公共溢出区。