王者荣耀铭文1和铭文2怎么调铭文

解析WeakHashMap与HashMap的区别详解
字体:[ ] 类型:转载 时间:
本篇文章是对WeakHashMap与HashMap的区别进行了详细的分析介绍,需要的朋友参考下
WeakHashMap,此种Map的特点是,当除了自身有对key的引用外,此key没有其他引用那么此map会自动丢弃此值,见实例:此例子中声明了两个Map对象,一个是HashMap,一个是WeakHashMap,同时向两个map中放入a、b两个对象,当HashMap& remove掉a 并且将a、b都指向null时,WeakHashMap中的a将自动被回收掉。出现这个状况的原因是,对于a对象而言,当HashMap& remove掉并且将a指向null后,除了WeakHashMap中还保存a外已经没有指向a的指针了,所以WeakHashMap会自动舍弃掉a,而对于b对象虽然指向了null,但HashMap中还有指向b的指针,所以WeakHashMap将会保留 代码如下:& import java.util.HashM& import java.util.I& import java.util.M& import java.util.WeakHashM& public class Test {& &&& public static void main(String[] args) throws Exception {& &&&&&&& String a = new String("a");& &&&&&&& String b = new String("b");& &&&&&&& Map weakmap = new WeakHashMap();& &&&&&&& Map map = new HashMap();& &&&&&&& map.put(a, "aaa");& &&&&&&& map.put(b, "bbb");& &&&&&&&&& &&&&&&& weakmap.put(a, "aaa");& &&&&&&& weakmap.put(b, "bbb");& &&&&&&& map.remove(a);& &&&&&&& a=& &&&&&&& b=& &&&&&&& System.gc();& &&&&&&& Iterator i = map.entrySet().iterator();& &&&&&&& while (i.hasNext()) {& &&&&&&&&&&& Map.Entry en = (Map.Entry)i.next();& &&&&&&&&&&& System.out.println("map:"+en.getKey()+":"+en.getValue());& &&&&&&& }& &&&&&&& Iterator j = weakmap.entrySet().iterator();& &&&&&&& while (j.hasNext()) {& &&&&&&&&&&& Map.Entry en = (Map.Entry)j.next();& &&&&&&&&&&& System.out.println("weakmap:"+en.getKey()+":"+en.getValue());& &&&&&&& }& &&& }& &&&&& }& 先把问题说清楚:WeakHashMap是主要通过expungeStaleEntries这个函数的来实现移除其内部不用的条目从而达到的自动释放内存的目的的.基本上只要对WeakHashMap的内容进行访问就会调用这个函数,从而达到清除其内部不在为外部引用的条目。但是如果预先生成了WeakHashMap,而在GC以前又不曾访问该WeakHashMap,那不是就不能释放内存了吗?对应的两个测试案例:WeakHashMapTest1: 代码如下:public class WeakHashMapTest1 {public static void main(String[] args) throws Exception {List&WeakHashMap&byte[][], byte[][]&& maps = new ArrayList&WeakHashMap&byte[][], byte[][]&&();for (int i = 0; i & 1000; i++) {WeakHashMap&byte[][], byte[][]& d = new WeakHashMap&byte[][], byte[][]&();d.put(new byte[], new byte[]);maps.add(d);System.gc();System.err.println(i);}}}由于Java默认内存是64M,所以再不改变内存参数的情况下,该测试跑不了几步循环就内存溢出了。果不其然,WeakHashMap这个时候并没有自动帮我们释放不用的内存。WeakHashMapTest2: 代码如下:public class WeakHashMapTest2 {public static void main(String[] args) throws Exception {List&WeakHashMap&byte[][], byte[][]&& maps = new ArrayList&WeakHashMap&byte[][], byte[][]&&();for (int i = 0; i & 1000; i++) {WeakHashMap&byte[][], byte[][]& d = new WeakHashMap&byte[][], byte[][]&();d.put(new byte[], new byte[]);maps.add(d);System.gc();System.err.println(i);for (int j = 0; j & j++) {System.err.println(j + " size" + maps.get(j).size());}}}} 这次测试输出正常,不在出现内存溢出问题.总结来说:WeakHashMap并不是你啥也干他就能自动释放内部不用的对象的,而是在你访问它的内容的时候释放内部不用的对象问题讲清楚了,现在我们来梳理一下.了解清楚其中的奥秘.WeakHashMap实现弱引用,是因为它的Entry&K,V&是继承自WeakReference&K&的在WeakHashMap$Entry&K,V&的类定义及构造函数里面是这样写的: 代码如下:private static class Entry&K,V& extends WeakReference&K& implements Map.Entry&K,V& Entry(K key, V value, ReferenceQueue&K& queue,int hash, Entry&K,V& next) { super(key, queue); this.value = this.hash = this.next = } 请注意它构造父类的语句:“super(key, queue);”,传入的是key,因此key才是进行弱引用的,value是直接强引用关联在this.value之中.在System.gc()时,key中的byte数组进行了回收,而value依然保持(value被强关联到entry上,entry又关联在map中,map关联在arrayList中.).如何证明key中的byte被回收了呢?可以通过内存溢出时导出的内存镜像进行分析,也可以通过如下的小测试得出结论:把上面的value用小对象代替, 代码如下:for (int i = 0; i & 10000; i++) { WeakHashMap&byte[][], Object& d = new WeakHashMap&byte[][], Object&(); d.put(new byte[], new Object()); maps.add(d); System.gc(); System.err.println(i); } 上面的代码,即使执行10000次也没有问题,证明key中的byte数组确实被回收了。for循环中每次都new一个新的WeakHashMap,在put操作后,虽然GC将WeakReference的key中的byte数组回收了,并将事件通知到了ReferenceQueue,但后续却没有相应的动作去触发 WeakHashMap 去处理 ReferenceQueue所以 WeakReference 包装的key依然存在在WeakHashMap中,其对应的value也当然存在。&那value是何时被清除的呢?对两个例子进行分析可知,例子二中的maps.get(j).size()触发了value的回收,那又如何触发的呢.查看WeakHashMap源码可知,size方法调用了expungeStaleEntries方法,该方法对vm要回收的的entry(quene中)进行遍历,并将entry的value置空,回收了内存.所以效果是key在GC的时候被清除,value在key清除后访问WeakHashMap被清除.疑问:key的quene与map的quene是同一个quene,poll操作会减少一个reference,那问题是key如果先被清除,expungeStaleEntries遍历quene时那个被回收的key对应的entry还能取出来么???关于执行System.GC时,key中的byte数据如何被回收了,请见WeakReference referenceQueneWeakHashMappublic class WeakHashMap&K,V&extends AbstractMap&K,V&implements Map&K,V&以弱键实现的基于哈希表的 Map。在 WeakHashMap 中,当某个键不再正常使用时,将自动移除其条目。更精确地说,对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。丢弃某个键时,其条目从映射中有效地移除,因此,该类的行为与其他的 Map 实现有所不同。null 值和 null 键都被支持。该类具有与 HashMap 类相似的性能特征,并具有相同的效能参数初始容量 和加载因子。像大多数集合类一样,该类是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。该类主要与这样的键对象一起使用,其 equals 方法使用 == 运算符来测试对象标识。一旦这种键被丢弃,就永远无法再创建了,所以,过段时间后在 WeakHashMap 中查找此键是不可能的,不必对其项已移除而感到惊讶。该类十分适合与 equals 方法不是基于对象标识的键对象一起使用,比如,String 实例。然而,对于这种可重新创建的键对象,键若丢弃,就自动移除 WeakHashMap 条目,这种表现令人疑惑。WeakHashMap 类的行为部分取决于垃圾回收器的动作,所以,几个常见的(虽然不是必需的)Map 常量不支持此类。因为垃圾回收器在任何时候都可能丢弃键,WeakHashMap 就像是一个被悄悄移除条目的未知线程。特别地,即使对 WeakHashMap 实例进行同步,并且没有调用任何赋值方法,在一段时间后 ,size 方法也可能返回较小的值,对于 isEmpty 方法,可能返回 false,然后返回 true,对于给定的键,containsKey 方法可能返回 true 然后返回 false,对于给定的键,get 方法可能返回一个值,但接着返回 null,对于以前出现在映射中的键,put 方法返回 null,而 remove 方法返回 false,对于键集、值集、项集进行的检查,生成的元素数量越来越少。WeakHashMap 中的每个键对象间接地存储为一个弱引用的指示对象。因此,不管是在映射内还是在映射之外,只有在垃圾回收器清除某个键的弱引用之后,该键才会自动移除。实现注意事项:WeakHashMap 中的值对象由普通的强引用保持。因此应该小心谨慎,确保值对象不会直接或间接地强引用其自身的键,因为这会阻止键的丢弃。注意,值对象可以通过 WeakHashMap 本身间接引用其对应的键;这就是说,某个值对象可能强引用某个其他的键对象,而与该键对象相关联的值对象转而强引用第一个值对象的键。处理此问题的一种方法是,在插入前将值自身包装在 WeakReferences 中,如:m.put(key, new WeakReference(value)),然后,分别用 get 进行解包。该类所有“collection 视图方法”返回的迭代器均是快速失败的:在迭代器创建之后,如果从结构上对映射进行修改,除非通过迭代器自身的 remove 或 add 方法,其他任何时间任何方式的修改,迭代器都将抛出 ConcurrentModificationException。因此,面对并发的修改,迭代器很快就完全失败,而不是冒着在将来不确定的时间任意发生不确定行为的风险。注意,迭代器的快速失败行为不能得到保证,一般来说,存在不同步的并发修改时,不可能作出任何坚决的保证。快速失败迭代器尽最大努力抛出 ConcurrentModificationException。因此,编写依赖于此异常程序的方式是错误的,正确做法是:迭代器的快速失败行为应该仅用于检测 bug。 注意1:null 值和 null 键都被支持。注意2:不是线程安全的。注意3:迭代器的快速失败行为不能得到保证。注意4:WeakHashMap是无序的。注意5:确保值对象不会直接或间接地强引用其自身的键,因为这会阻止键的丢弃。但是,值对象可以通过 WeakHashMap 本身间接引用其对应的键;这就是说,某个值对象可能强引用某个其他的键对象,而与该键对象相关联的值对象转而强引用第一个值对象的键,这时就形成了环路。处理此问题的一种方法是,在插入前将值自身包装在WeakReferences中,如:m.put(key, new WeakReference(value)),然后,分别用 get 进行解包。如实例1.实例1: 代码如下:import java.lang.ref.WeakRimport java.util.LinkedHashMimport java.util.Mimport java.util.Rimport java.util.WeakHashMimport java.util.concurrent.ConcurrentLinkedQpublic class Test {&/**& * @param args& */&public static void main(String[] args) {& WeakHashMap&Integer,WeakReference&People&& map=new WeakHashMap&Integer,WeakReference&People&&();& People p1=new People("robin",1,28);& People p2=new People("robin",2,29);& People p3=new People("harry",3,30);& map.put(new Integer(100), new WeakReference&People&(p1));& map.put(new Integer(101), new WeakReference&People&(p2));& map.put(new Integer(1), new WeakReference&People&(p3));& for(WeakReference&People& rp:map.values())& {&& People p= rp.get();&& System.out.println("people:"+p);& }&}}class People{&S&&&public People(String name,int id)&{& this(name,id,0);&}&public People(String name,int id,int age)&{& this.name=& this.id=& this.age=&}&public String toString()&{& return id+name+&}&public boolean equals(Object o)&{& if(o==null)&&& if(!(o instanceof People))&&& People p=(People)o;& boolean res=name.equals(p.name);& if(res)&& System.out.println("name "+name+" is double");& else&& System.out.println(name+" vS "+p.name);&&}&public int hashCode()&{& return name.hashCode();&}}
您可能感兴趣的文章:
大家感兴趣的内容
12345678910
最近更新的内容
常用在线小工具ArrayList线程不安全分析 -
- ITeye技术网站
博客分类:
一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。
那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。线程不安全的例子:
public class ArrayListInThread implements Runnable {
List&String& list1 = new ArrayList&String&(1);// not thread safe//
List&String& list1 = Collections.synchronizedList(new ArrayList&String&());// thread safe
public void run() {
Thread.sleep((int)(Math.random() * 2));
catch (InterruptedException e) {
e.printStackTrace();
list1.add(Thread.currentThread().getName());
public static void main(String[] args) throws InterruptedException {
ThreadGroup group = new ThreadGroup("testgroup");
ArrayListInThread t = new ArrayListInThread();
for (int i = 0; i & <span style="color: #; i++) {
Thread th = new Thread(group, t, String.valueOf(i));
th.start();
while (group.activeCount() & 0) {
Thread.sleep(10);
System.out.println();
System.out.println(t.list1.size()); // it should be 10000 if thread safe collection is used.
wsmajunfeng
浏览: 224386 次
来自: 杭州
对我的理解起了很大的帮助
太棒了,谢谢分享
讲的,看到的最清晰明白的。
总结非常到位,谢谢分享。[Java]关于HashMap的多线程不安全 - My Tech Disk - ITeye技术网站
&&& HashMap不是多线程安全的,这个貌似大家都知道,但是这是不是意味着,只要在多线程环境下使用到HashMap,那就得套上syncronized呢?
&& 引用 Hash table based implementation of the Map interface. This implementation provides all of the optional map operations, and permits null values and the null key. (The HashMap& class is roughly equivalent to Hashtable, except that it is unsynchronized and permits nulls.) This class makes no guarantees as to
in particular, it does not guarantee that the order will remain constant over time.
&&& HashMap是基于Hash Table的一种Map实现,允许null values和null key(它跟HashTable的区别在于它是非同步的,而且允许null key和null values),HashMap不保证Map的顺序,甚至不能保证Map的顺序在运行过程中保持不变。
&&& 引用Note that this implementation is not synchronized. If multiple threads access this map concurrently, and at least one of the threads modifies the map structurally, it must be synchronized externally. (A structural modification is any operation that adds or deletes
merely changing the value associated with a key that an instance already contains is not a structural modification.) This is typically accomplished by synchronizing on some object that naturally encapsulates the map. If no such object exists, the map should be "wrapped" using the Collections.synchronizedMap method. This is best done at creation time, to prevent accidental unsynchronized access to the map:
Map m = Collections.synchronizedMap(new HashMap(...));
&&& 这种实现不是同步的,如果多个线程同时访问这个Map,而且至少一个线程对Map进行结构性的修改(增加,删除操作,update不算),那么它必须在外部进行同步,通常这种同步是通过包含这个Map的外部对象来实现的,如果该外部对象不存在,那么这个Map需要通过Collections.synchronizedMap来保证同步(最好在创建的时候)。
&& 引用 The iterators returned by all of this class's "collection view methods" are fail-fast: if the map is structurally modified at any time after the iterator is created, in any way except through the iterator's own remove or add methods, the iterator will throw a ConcurrentModificationException. Thus, in the face of concurrent modification, the iterator fails quickly and cleanly, rather than risking arbitrary, non-deterministic behavior at an undetermined time in the future.
&&& Note that the fail-fast behavior of an iterator cannot be guaranteed as it is, generally speaking, impossible to make any hard guarantees in the presence of unsynchronized concurrent modification. Fail-fast iterators throw ConcurrentModificationException on a best-effort basis. Therefore, it would be wrong to write a program that depended on this exception for its correctness: the fail-fast behavior of iterators should be used only to detect bugs.
&& 话说,没看懂~~
关于fail-fast,中文意思是快速失败,没搜到官方的解释(搜索技术不好?),找到这个问答
引用An iterator is considered fail-fast& if it throws a ConcurrentModificationException under either of the following two conditions:
&& 1. In multithreaded processing: if one thread is trying to modify a Collection while another thread is iterating over it.
&& 2. In single-threaded or in multithreaded processing: if after the creation of the Iterator, the container is modified at any time by any method other than the Iterator's own remove or add methods.
来自ibm developerworks上对java.util.concurrent包的说明片段:
&&&&& java.util 包中的集合类都返回 fail-fast 迭代器,这意味着它们假设线程在集合内容中进行迭代时,集合不会更改它的内容。如果 fail-fast 迭代器检测到在迭代过程中进行了更改操作,那么它会抛出 ConcurrentModificationException ,这是不可控异常。
&&&&& 在迭代过程中不更改集合的要求通常会对许多并发应用程序造成不便。相反,比较好的是它允许并发修改并确保迭代器只要进行合理操作,就可以提供集合的一致视图,如 java.util.concurrent 集合类中的迭代器所做的那样。
&&&& java.util.concurrent 集合返回的迭代器称为弱一致的(weakly consistent)迭代器。对于这些类,如果元素自从迭代开始已经删除,且尚未由 next() 方法返回,那么它将不返回到调用者。如果元素自迭代开始已经添加,那么它可能返回调用者,也可能不返回。在一次迭代中,无论如何更改底层集合,元素不会被返回两次。
&& 算了,如果多线程用到HashMap,如果不能保证是只读的,还是给它套个syncronized好了~~
&& 丫的我还真没钻研精神~~
weiqiang.yang
浏览: 84471 次
来自: 杭州
楼主 就再也没有解决吗?我现在是30个线程 同时去访问mysq ...
楼主后来是怎么解决问题的,这个我也遇到了
丫的我还真没钻研精神~~
不错,,帮大忙了。
/show/detai ...众所周知,hashmap线程不安全而hashtable线程安全。最近在看并发编程就稍微研究了一下。先看一段JAVAAPI中对hashmap的介绍:
*注意,此实现不是同步的。如果多个线程同时访问此映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:
Map m = Collections.synchronizedMap(new HashMap(…));*
hashmap的底层是一个链表散列(数组与链表的结合体)。
void addEntry(int hash, K key, V value, int bucketIndex) {
Entry&K,V& e = table[bucketIndex];
table[bucketIndex] = new Entry&K,V&(hash, key, value, e);
if (size++ &= threshold)
resize(2 * table.length);
hashmap在调用put方法时会调用上面方法,假若现在同时有A线程和B线程同时获得了同一个头节点并对其进行修改,这样A写入头节点之后B也写入头节点则会将A的操作覆盖
final Entry&K,V& removeEntryForKey(Object key) {
int hash = (key == null) ? 0 : hash(key.hashCode());
int i = indexFor(hash, table.length);
Entry&K,V& prev = table[i];
Entry&K,V& e =
while (e != null) {
Entry&K,V& next = e.next;
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
if (prev == e)
table[i] = next;
prev.next = next;
e.recordRemoval(this);
这上面是删除操作,同样的如果一个头节点的元素同时被两个线程删除,则又会造成混乱。
`void resize(int newCapacity) {
Entry[] oldTable =
int oldCapacity = oldTable.
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
Entry[] newTable = new Entry[newCapacity];
transfer(newTable);
table = newT
threshold = (int)(newCapacity * loadFactor);
以上是hashmap的增加容量的操作,如果同时A,B两个线程都要执行resize()操作,A线程已经进行在原基础的扩容,B线程也在原基础上扩容,这样就会造成线程不安全。
下面写一个例子来验证一下
import java.util.HashM
import java.util.R
import java.util.concurrent.atomic.AtomicI
public class hashMap {
static void dos(){
HashMap&Long,String& ss=new HashMap&Long,String&();
final AtomicInteger in=new AtomicInteger(0);
ss.put(0L, "ssssssss");
for(int i=0;i&200;i++){
new Thread(new Runnable() {
public void run() {
ss.put(System.nanoTime()+new Random().nextLong(),"ssssssssssss");
String s=ss.get(0L);
if(s==null){
in.incrementAndGet();
}).start();
System.out.println(in);
public static void main(String[] args) {
for(int i=0;i&10;i++){
结果并不都是0
但是如果将hashmap的初始大小设的大一些就不会出现这种情况。
下来研究一下如何使hashmap线程安全
//两种方式让hashmap线程安全
1.第一种办法api中已经有指出
如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的不同步访问,如下所示:
Map m = Collections.synchronizedMap(new HashMap(…));
即使用Collections.synchronizedMap()来返回一个新的map,但是这个返回的是不是hashmap,而是一个map的实现
第二种办法是改写hashmap。//暂时正在研究源码。。。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1565次
排名:千里之外
(1)(3)(11)【java并发】造成HashMap非线程安全的原因
&#xe614;博客专家
【java并发】造成HashMap非线程安全的原因
● 编程语言
------【Java进阶】
0. 写在前面
  在前面我的一篇总结文章中提到,为了数据能在线程范围内使用,我用了HashMap来存储不同线程中的数据,key为当前线程,value为当前线程中的数据。我取的时候根据当前线程名从HashMap中取即可。
  因为当初学习和HashTable源码的时候,知道HashTable是线程安全的,因为里面的方法使用了synchronized进行同步,但是HashMap没有,所以HashMap是非线程安全的。在上面提到的例子中,我想反正不用修改HashMap,只需要从中取值即可,所以不会有线程安全问题,但是我忽略了一个步骤:我得先把不同线程的数据存到HashMap中吧,这个存就可能出现问题,虽然我存的时候key使用了不同的线程名字,理论上来说是不会冲突的,但是这种设计或者思想本来就不够严谨。这也是由于一个网友看到我的那篇文章后给我提出的问题,我后来仔细推敲了下,重新温习了下HashMap的源码,再加上网上查的一些资料,在这里总结一下HashMap到底什么时候可能出现线程安全问题。
  我们知道HashMap底层是一个Entry数组,当发生hash冲突的时候,HashMap是采用链表的方式来解决的,在对应的数组位置存放链表的头结点。对链表而言,新加入的节点会从头结点加入。javadoc中有一段关于HashMap的描述:
此实现不是同步的。如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须保持外部同步。(结构上的修改是指添加或删除一个或多个映射关系的任何操作;仅改变与实例已经包含的键关联的值不是结构上的修改。)这一般通过对自然封装该映射的对象进行同步操作来完成。如果不存在这样的对象,则应该使用 Collections.synchronizedMap 方法来“包装”该映射。最好在创建时完成这一操作,以防止对映射进行意外的非同步访问,如下所示:
Map m = Collections.synchronizedMap(new HashMap(...));
  可以看出,解决HashMap线程安全问题的方法很简单,下面我简单分析一下可能会出现线程问题的一些地方。
1. 向HashMap中插入数据的时候
  在HashMap做put操作的时候会调用到以下的方法:
void addEntry(int hash, K key, V value, int bucketIndex) {
if ((size &= threshold) && (null != table[bucketIndex])) {
resize(2 * table.length);
hash = (null != key) ? hash(key) : 0;
bucketIndex = indexFor(hash, table.length);
createEntry(hash, key, value, bucketIndex);
void createEntry(int hash, K key, V value, int bucketIndex) {
Entry&K,V& e = table[bucketIndex];
table[bucketIndex] = new Entry&&(hash, key, value, e);
  现在假如A线程和B线程同时进入addEntry,然后计算出了相同的哈希值对应了相同的数组位置,因为此时该位置还没数据,然后对同一个数组位置调用createEntry,两个线程会同时得到现在的头结点,然后A写入新的头结点之后,B也写入新的头结点,那B的写入操作就会覆盖A的写入操作造成A的写入操作丢失。
2. HashMap扩容的时候
  还是上面那个addEntry方法中,有个扩容的操作,这个操作会新生成一个新的容量的数组,然后对原数组的所有键值对重新进行计算和写入新的数组,之后指向新生成的数组。来看一下扩容的源码:
void resize(int newCapacity) {
Entry[] oldTable =
int oldCapacity = oldTable.
if (oldCapacity == MAXIMUM_CAPACITY) {
threshold = Integer.MAX_VALUE;
Entry[] newTable = new Entry[newCapacity];
transfer(newTable, initHashSeedAsNeeded(newCapacity));
table = newT
threshold = (int)Math.min(newCapacity * loadFactor, MAXIMUM_CAPACITY + 1);
  那么问题来了,当多个线程同时进来,检测到总数量超过门限值的时候就会同时调用resize操作,各自生成新的数组并rehash后赋给该map底层的数组table,结果最终只有最后一个线程生成的新数组被赋给table变量,其他线程的均会丢失。而且当某些线程已经完成赋值而其他线程刚开始的时候,就会用已经被赋值的table作为原始数组,这样也会有问题。所以在扩容操作的时候也有可能会引起一些并发的问题。
3. 删除HashMap中数据的时候
  删除键值对的源代码如下:
public V remove(Object key) {
Entry&K,V& e = removeEntryForKey(key);
return (e == null ? null : e.value);
final Entry&K,V& removeEntryForKey(Object key) {
if (size == 0) {
return null;
int hash = (key == null) ? 0 : hash(key);
int i = indexFor(hash, table.length);
Entry&K,V& prev = table[i];
Entry&K,V& e =
while (e != null) {
Entry&K,V& next = e.
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k)))) {
modCount++;
if (prev == e)
table[i] =
prev.next =
e.recordRemoval(this);
  删除这一块可能会出现两种线程安全问题,第一种是一个线程判断得到了指定的数组位置i并进入了循环,此时,另一个线程也在同样的位置已经删掉了i位置的那个数据了,然后第一个线程那边就没了。但是删除的话,没了倒问题不大。
  再看另一种情况,当多个线程同时操作同一个数组位置的时候,也都会先取得现在状态下该位置存储的头结点,然后各自去进行计算操作,之后再把结果写会到该数组位置去,其实写回的时候可能其他的线程已经就把这个位置给修改过了,就会覆盖其他线程的修改。
  其他地方还有很多可能会出现线程安全问题,我就不一一列举了,总之HashMap是非线程安全的,在高并发的场合使用的话,要用Collections.synchronizedMap进行包装一下。另外还得感谢那位网友,我对HashMap线程安全问题的认识又进了一步~
—–乐于分享,共同进步!
—–更多文章请看:
我的热门文章
即使是一小步也想与你分享

我要回帖

更多关于 王者荣耀铭文法师通用 的文章

 

随机推荐