`
378629846
  • 浏览: 215687 次
  • 性别: Icon_minigender_1
  • 来自: 哈尔滨
社区版块
存档分类
最新评论

用闭锁测试HashMap的并发写入问题

    博客分类:
  • java
阅读更多

今天无意中看到以前写的代码,是一个单例的工厂模式实现,代码片段如下:

 

private static Map daoMap = new HashMap(); 
	
public static Dao createDao(String className) {
	Dao dao = (Dao) daoMap.get(className);
	if (dao != null) {
		return dao;
	} else {
		dao = (Dao) Class.forName(className).newInstance();
		daoMap.put(className, dao);
		return dao;
	}
}

 这段代码是在一个Java EE项目的一个工厂类里发现的,今天看来,在并发稍大点儿的时候,它就会出现问题,因为HashMap是线程不安全的。于是写了段代码测试一下HashMap在并发写入时会出什么问题。

测试类:

 

public class Test {
	static Map<String,String> map = new HashMap();
	static int N = 1000;
	static CountDownLatch startSignal = new CountDownLatch(1);//闭锁
	static CountDownLatch doneSignal = new CountDownLatch(N);
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		for (int i = 0; i < N; i++) {
			final int j = i;
			new Thread(new Runnable(){
				public void run() {
					try {
						startSignal.await();//使当前线程在锁存器倒计数至零之前一直等待
					} catch (InterruptedException e) {
						e.printStackTrace();
					} 
					map.put(Thread.currentThread().getId()+""+j, "test");
					doneSignal.countDown();
				}
			}).start();
		}
		startSignal.countDown();//递减锁存器的计数,如果计数到达零,则释放所有等待的线程
		try {
			doneSignal.await();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println("done,size:" + map.size());
	}

}

 上面的测试类里用到了一个jdk1.5里出现的类CountDownLatch,闭锁。因为我们要在所有线程结束后,来检验map的size。

测试结果:

 

done,size:948

 可见,HashMap出了问题,1000个不重复的元素只放进去948个,如果加大N的值,程序就会陷入死循环。

解决方案是将HashMap换成ConcurrentHashMap,也是jdk1.5以后才有的类。它的并发性能要比Hashtable好。

使用N=7000,进行测试:

ConcurrentHashMap测试的最好结果是:

 

done,size:7000, time:1735

Hashtable的最好结果是:

 

done,size:7000, time:1800

如果N越大,效果会越明显。

 

2
6
分享到:
评论
7 楼 378629846 2012-08-29  
378629846 写道
wangdf_jee 写道
N=10000时:Collections.synchronizedMap(new HashMap<String,String>()) 时间是8295ms
而 new ConcurrentHashMap<String,String>() 是8760ms.


N=1000时:Collections.synchronizedMap(new HashMap<String,String>()) 时间是190ms
而 new ConcurrentHashMap<String,String>() 是195ms.



这个没用过,回头可以试试!


在我的电脑上效果不如ConcurrentHashMap:

done,size:7000, time:1821

看了看源码,Collections.synchronizedMap()只是在HashMap的基础上增加了synchronized,代码如下:
public V put(K key, V value) {
       synchronized(mutex) {return m.put(key, value);}
}
从实现看并发性能应该和Hashtable差不多。而ConcurrentHashMap用的是分离锁,默认实现是使用了一个包含16个锁的Array,每个锁都守护Hash Bucket的1/16,这样就把锁的竞争减少到原来的1/16;见如下代码:
public V put(K key, V value) {
        if (value == null)
            throw new NullPointerException();
        int hash = hash(key);
        return segmentFor(hash).put(key, hash, value, false);
    }
final Segment<K,V> segmentFor(int hash) {
        return (Segment<K,V>) segments[(hash >>> segmentShift) & segmentMask];
    }
还有,需要注意的是,ConcurrentHashMap为了提高性能和吞吐量,相对弱化了一些需要对整个map进行操作的方法,如:size和isEmpty,这时的size可能仅是个估算值。用的时候需要进行取舍
6 楼 378629846 2012-08-29  
hekuilove 写道
lz知道Collections.synchronizedMap吗

听说过,没用过,有时间可以研究一下。
5 楼 hekuilove 2012-08-28  
lz知道Collections.synchronizedMap吗
4 楼 378629846 2012-08-28  
wangdf_jee 写道
N=10000时:Collections.synchronizedMap(new HashMap<String,String>()) 时间是8295ms
而 new ConcurrentHashMap<String,String>() 是8760ms.


N=1000时:Collections.synchronizedMap(new HashMap<String,String>()) 时间是190ms
而 new ConcurrentHashMap<String,String>() 是195ms.



这个没用过,回头可以试试!
3 楼 wangdf_jee 2012-08-28  
N=10000时:Collections.synchronizedMap(new HashMap<String,String>()) 时间是8295ms
而 new ConcurrentHashMap<String,String>() 是8760ms.


N=1000时:Collections.synchronizedMap(new HashMap<String,String>()) 时间是190ms
而 new ConcurrentHashMap<String,String>() 是195ms.
2 楼 LvQing 2012-08-28  
很好,学习了 一下 CountDownLatch  倒数计数锁
1 楼 zjhlht 2012-08-28  
支持一下好的总结,好的重构精神

相关推荐

    关于如何解决HashMap线程安全问题的介绍

    4. 避免在多线程环境中直接使用HashMap:如果你确定不需要在多线程环境下共享HashMap,那么可以考虑局部变量的方式,只在单个线程中使用HashMap,这样就无需担心线程安全问题。 总结起来,理解HashMap的线程不安全...

    高级程序员必会的HashMap的线程安全问题,适用于0~2年的.7z

    然而,对于多线程环境,HashMap并不是线程安全的,这在并发编程中可能会引发一系列问题。本篇将深入探讨HashMap的线程安全问题,并提供相关的解决方案。 首先,我们需要了解HashMap在多线程环境下可能出现的问题: ...

    java的hashMap多线程并发情况下扩容产生的死锁问题解决.docx

    此外,引入了ConcurrentHashMap类,这是一个专门为多线程设计的高效容器,其内部使用分段锁策略,可以在并发环境下保证线程安全,避免了类似HashMap扩容引发的死锁问题。 如果你在多线程环境中使用HashMap并遇到...

    hashmap面试题_hashmap_

    5. HashMap的并发问题如何解决? 答:在多线程环境下,可以使用ConcurrentHashMap,它是线程安全的HashMap实现。 五、HashMap与HashSet的关系 HashSet基于HashMap实现,每个元素作为HashMap的一个键,值为null。...

    TBB 开源库及并发 Hashmap 的使用

    TBB 开源库及并发 Hashmap 的使用 TBB 开源库是 Intel 公司开发的开源并行计算库,用于在多核平台上进行并行化程序的开发。该库使用标准 C++ 编写,提供了一个灵活的框架来实现数据并行计算。使用 TBB 可以使用户...

    ibatis 用HashMap解决resultClass映射

    通过上述介绍,我们可以看到使用 `HashMap` 解决 ibatis 中的动态列名和列数的问题是一种非常实用的方法。它不仅避免了为每种情况创建实体类的繁琐工作,还提高了代码的灵活性和可维护性。希望本文能够帮助你在实际...

    HashMap介绍和使用

    ### HashMap介绍和使用详解 #### 一、HashMap的数据结构 HashMap是Java集合框架的一个重要组成部分,它实现了Map接口,能够存储键值对映射。在Java编程语言中,最基本的数据结构有两种:数组和引用(模拟指针)。...

    hashmap-thread-test:测试 Java HashMap 是否是线程安全的

    然而,`HashMap`并非线程安全,这意味着在多线程环境中直接使用`HashMap`可能会导致数据不一致、并发问题,甚至程序崩溃。本项目“hashmap-thread-test”显然是为了测试和展示这一特性。 ### Java HashMap 的特性 ...

    马士兵老师HashMap学习笔记

    总的来说,马士兵老师的HashMap学习笔记不仅涵盖了HashMap的基础知识,还深入探讨了其在不同JDK版本中的实现细节,特别是并发环境下的问题和解决方案。理解这些内容对于提升Java编程技能和优化代码性能具有重要价值...

    HashMap和HashTable的区别和不同

    如果多个线程并发地访问并修改`HashMap`,则可能导致数据不一致性问题。为了在多线程环境中安全地使用`HashMap`,开发者需要自己负责同步,例如使用`Collections.synchronizedMap(new HashMap,V&gt;())`创建线程安全的`...

    HashMap的数据结构

    4. **并发问题**:HashMap不是线程安全的,这意味着在多线程环境中,同时对HashMap进行读写操作可能会导致数据不一致。如果需要线程安全的哈希表,可以使用`ConcurrentHashMap`。 5. **null值**:HashMap允许键和值...

    Java中HashMap详解(通俗易懂).doc

    - 注意HashMap的并发问题,如果在多线程环境下,使用Collections.synchronizedMap()或使用ConcurrentHashMap来保证线程安全。 总结起来,HashMap和HashSet是Java集合框架的重要组成部分,它们利用哈希技术提供了...

    用hashmap实现词典查询

    如果存在冲突(多个键映射到同一个位置),HashMap通常会使用链表或红黑树来处理这些冲突,确保查找效率仍然保持较高。 为了实现词典查询,我们需要以下步骤: 1. **读取字典文件**:字典文件通常包含一系列词条,...

    HASHMAP缓存.txt

    文档中的代码示例展示了一种使用HashMap作为缓存的常见模式:单例模式下的缓存管理。以下是对代码关键部分的解析: 1. **初始化HashMap**:`protected static final HashMap map = new HashMap();` 这一行代码创建...

    HashMap总结

    HashMap 使用链表来解决哈希冲突的问题,即当两个键的哈希值相同时,HashMap 使用链表来存储这些键值对。 HashMap 的应用场景 1. 缓存机制:HashMap 可以用来实现缓存机制,例如缓存用户信息、缓存查询结果等。 2....

    通过代码证明HashMap是线程不安全的(只用了一个Java文件)

    `HashMap`在设计时并未考虑线程安全,因此在并发场景下,如果不采取适当的同步措施,可能会遇到诸如数据丢失、死锁等问题。 `HashMap`的核心实现是基于哈希表的,它使用了数组和链表的数据结构。当多个线程同时对`...

    hashmap实现原理

    哈希映射(HashMap)是Java编程语言中广泛使用的数据结构之一,主要提供键值对的存储和查找功能。HashMap的实现基于哈希表的概念,它通过计算对象的哈希码来快速定位数据,从而实现了O(1)的平均时间复杂度。在深入...

    hashtable和hashmap的区别

    因此,在多线程环境中使用`HashMap`时,如果不采取额外的同步措施,可能会导致数据不一致或其他并发问题。 #### 2. 同步机制 - **Hashtable**: 使用内部同步机制来确保线程安全,这意味着在执行关键操作时会锁定...

    HashMap与HashTable区别

    - **HashMap**: 相对于`HashTable`,`HashMap`在单线程或低并发情况下具有更好的性能。这是因为`HashMap`没有同步开销,因此操作速度更快。 #### 四、内部实现细节 - **HashTable**: 内部使用了`Entry`类来表示...

Global site tag (gtag.js) - Google Analytics