您当前的位置: 首页 >  安全

蓝不蓝编程

暂无认证

  • 4浏览

    0关注

    706博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

HashMap非线程安全分析

蓝不蓝编程 发布时间:2014-04-20 16:09:49 ,浏览量:4

通过各方资料了解,HashMap不是线程安全的,但是为什么不是线程安全的,在什么情况下会出现问题呢?

1. 下面对HashMap做一个实验,两个线程,并发写入不同的值,key和value相同,最后再看看value和key是否相等。

import java.util.HashMap;

public class TestHashMap {

	public static final HashMap hashMap = new HashMap();

	public static void main(String[] args) throws InterruptedException {

		// 线程一
		Thread t1 = new Thread() {
			public void run() {
				for (int i = 0; i < 25; i++) {
					hashMap.put(String.valueOf(i), String.valueOf(i));
				}
			}
		};

		// 线程二
		Thread t2 = new Thread() {
			public void run() {
				for (int j = 25; j < 50; j++) {
					hashMap.put(String.valueOf(j), String.valueOf(j));
				}
			}
		};

		t1.start();
		t2.start();

		// 主线程休眠1秒钟,以便t1和t2两个线程将firstHashMap填装完毕。
		Thread.sleep(1000);

		for (int i = 0; i < 50; i++) {
			// 如果key和value不同,说明在两个线程put的过程中出现异常。
			if (!String.valueOf(i).equals(hashMap.get(String.valueOf(i)))) {
				System.err.println("出现多线程异常,序号:"+i);
			}
		}
	}
}

经过多次测试,都会出现类似下面的错误:

出现多线程异常,序号:0 出现多线程异常,序号:1 出现多线程异常,序号:3 出现多线程异常,序号:4

2. 为什么会导致这样的情况

  1) 查看HashMap的put方法

   

    public V put(K key, V value) {
        if (key == null)
            return putForNullKey(value);
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }

        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

因为put方法没有加synchronized方法,在执行modCount++,addEntry时都有可能出现问题,modCount代码比较简单,就不深究了,下面再看看addEntry函数。

  2)addEntry函数

    void addEntry(int hash, K key, V value, int bucketIndex) {
	Entry e = table[bucketIndex];
        table[bucketIndex] = new Entry(hash, key, value, e);
        if (size++ >= threshold)
            resize(2 * table.length);
    }

当容量达到阈值时,就会对map进行扩容,然后将原有值拷贝到新的值中;可以想象,如果两个线程同时对map进行扩容,将会带来巨大的问题,如数据丢失。

基于这点原因考虑,如果map本身的大小就比较大,不会扩容,那情况如何?

修改上述代码样例中的hashMap的构造函数,带上初始大小

public static final HashMap hashMap = new HashMap(50);

再次测试,基本没有出现并发的问题。

不过理论上看应该还是有问题的,只是出现的几率减小了,对于多线程情况下,可以使用HashTable或者通过java.util.Collections.synchronizedMap(map)函数对map进行封装或者使用ConcurrentHashMap类

参考资料: 

http://blog.sina.com.cn/s/blog_4a1f59bf0100o98k.html http://www.iteye.com/topic/656670



关注
打赏
1639405877
查看更多评论
立即登录/注册

微信扫码登录

0.0371s