HashMap为什么是线程不安全的?

前端之家收集整理的这篇文章主要介绍了HashMap为什么是线程不安全的?前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。

<p style="color:rgb(51,51,51);font-family:Arial;font-size:14px;">一直以来只是知道HashMap是线程不安全的,但是到底HashMap为什么线程不安全,多线程并发的时候在什么情况下可能出现问题?


<p style="color:rgb(51,51);font-family:Arial;font-size:14px;">HashMap底层是一个Entry数组,当发生hash冲突的时候,hashmap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。


<p style="color:rgb(51,51);font-family:Arial;font-size:14px;">javadoc中关于hashmap的一段描述如下:


<p style="color:rgb(51,51);font-family:Arial;font-size:14px;">


<p style="color:rgb(51,51);font-size:14px;font-family:Simsun;">此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 <a href="http://www.ostools.net/uploads/apidocs/jdk-zh/java/util/Collections.html#synchronizedMap(java.util.Map)" rel="nofollow" style="color:rgb(51,102,153);text-decoration:none;">Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:


<pre style="color:rgb(51,51);font-size:14px;"> Map m = Collections.synchronizedMap(new HashMap(...));
<br style="color:rgb(51,51);font-family:Arial;font-size:14px;"><p style="color:rgb(51,51);font-family:Arial;font-size:14px;">


<p style="color:rgb(51,51);font-family:Arial;font-size:14px;">1、


<p style="color:rgb(51,51);font-family:Arial;font-size:14px;">


<div class="dp-Highlighter bg_java" style="font-family:Consolas,'Courier New',Courier,mono,serif;color:rgb(51,51);">
<div class="bar">
<div class="tools" style="font-size:9px;line-height:normal;font-family:Verdana,Geneva,Arial,Helvetica,sans-serif;color:#C0C0C0;border-left-width:3px;border-left-style:solid;border-left-color:rgb(108,226,108);">
[java] <a href="http://blog.csdn.net/mydreamongo/article/details/8960667#" rel="nofollow" class="ViewSource" title="view plain" style="color:rgb(160,160,160);text-decoration:none;border:none;display:inline-block;width:16px;text-indent:-2000px;">view
plain <a href="http://blog.csdn.net/mydreamongo/article/details/8960667#" rel="nofollow" class="CopyToClipboard" title="copy" style="color:rgb(160,160);text-decoration:none;border:none;display:inline-block;width:16px;text-indent:-2000px;">copy
<div style="width:18px;z-index:99;">

  1.  e = table[bucketIndex];  
  2. (hash, key, value, e);  
  3. = threshold)  

调用到以上的方法。现在假如A线程和B线程同时对同一个数组位置调用addEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失

 removeEntryForKey(Object key) {  

  •  prev = table[i];  
  •  e = prev;  
  •  next = e.next;  
  • 删除键值对的代码如上:

    修改过了,就会覆盖其他线程的修改

    数量超过门限值的时候会调用一个resize操作,代码如下:

  • 生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。


    数量超过门限值的时候就会同时调用resize操作,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。

    猜你在找的Java相关文章